azure-storage-blob 12.25.0b1__py3-none-any.whl → 12.26.0__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 +94 -41
- azure/storage/blob/_blob_client_helpers.py +19 -4
- 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 +1 -1
- azure/storage/blob/_generated/_configuration.py +2 -2
- azure/storage/blob/_generated/_serialization.py +3 -3
- azure/storage/blob/_generated/aio/_azure_blob_storage.py +1 -1
- azure/storage/blob/_generated/aio/_configuration.py +2 -2
- azure/storage/blob/_generated/aio/operations/_append_blob_operations.py +5 -4
- azure/storage/blob/_generated/aio/operations/_blob_operations.py +5 -25
- azure/storage/blob/_generated/aio/operations/_block_blob_operations.py +9 -7
- azure/storage/blob/_generated/aio/operations/_container_operations.py +1 -19
- azure/storage/blob/_generated/aio/operations/_page_blob_operations.py +5 -10
- azure/storage/blob/_generated/aio/operations/_service_operations.py +1 -8
- azure/storage/blob/_generated/models/__init__.py +2 -0
- azure/storage/blob/_generated/models/_azure_blob_storage_enums.py +6 -0
- azure/storage/blob/_generated/operations/_append_blob_operations.py +12 -9
- azure/storage/blob/_generated/operations/_blob_operations.py +32 -49
- azure/storage/blob/_generated/operations/_block_blob_operations.py +21 -13
- azure/storage/blob/_generated/operations/_container_operations.py +19 -37
- azure/storage/blob/_generated/operations/_page_blob_operations.py +17 -19
- azure/storage/blob/_generated/operations/_service_operations.py +9 -17
- azure/storage/blob/_lease.py +1 -0
- azure/storage/blob/_quick_query_helper.py +20 -24
- 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 +45 -43
- 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 +87 -61
- azure/storage/blob/_shared/base_client_async.py +58 -51
- azure/storage/blob/_shared/constants.py +1 -1
- azure/storage/blob/_shared/models.py +93 -92
- azure/storage/blob/_shared/parser.py +3 -3
- azure/storage/blob/_shared/policies.py +176 -145
- azure/storage/blob/_shared/policies_async.py +59 -70
- azure/storage/blob/_shared/request_handlers.py +51 -47
- azure/storage/blob/_shared/response_handlers.py +49 -45
- azure/storage/blob/_shared/shared_access_signature.py +67 -71
- azure/storage/blob/_shared/uploads.py +56 -49
- azure/storage/blob/_shared/uploads_async.py +72 -61
- azure/storage/blob/_shared_access_signature.py +3 -1
- azure/storage/blob/_version.py +1 -1
- azure/storage/blob/aio/__init__.py +3 -2
- azure/storage/blob/aio/_blob_client_async.py +241 -44
- 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 +16 -12
- azure/storage/blob/aio/_lease_async.py +1 -0
- azure/storage/blob/aio/_quick_query_helper_async.py +194 -0
- {azure_storage_blob-12.25.0b1.dist-info → azure_storage_blob-12.26.0.dist-info}/METADATA +7 -7
- azure_storage_blob-12.26.0.dist-info/RECORD +85 -0
- {azure_storage_blob-12.25.0b1.dist-info → azure_storage_blob-12.26.0.dist-info}/WHEEL +1 -1
- azure_storage_blob-12.25.0b1.dist-info/RECORD +0 -84
- {azure_storage_blob-12.25.0b1.dist-info → azure_storage_blob-12.26.0.dist-info}/LICENSE +0 -0
- {azure_storage_blob-12.25.0b1.dist-info → azure_storage_blob-12.26.0.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,
|
@@ -65,27 +68,42 @@ _SERVICE_PARAMS = {
|
|
65
68
|
|
66
69
|
|
67
70
|
class StorageAccountHostsMixin(object):
|
71
|
+
|
68
72
|
_client: Any
|
73
|
+
_hosts: Dict[str, str]
|
74
|
+
|
69
75
|
def __init__(
|
70
76
|
self,
|
71
77
|
parsed_url: Any,
|
72
78
|
service: str,
|
73
|
-
credential: Optional[
|
74
|
-
|
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,
|
75
90
|
) -> None:
|
76
91
|
self._location_mode = kwargs.get("_location_mode", LocationMode.PRIMARY)
|
77
|
-
self._hosts = kwargs.get("_hosts")
|
92
|
+
self._hosts = kwargs.get("_hosts", {})
|
78
93
|
self.scheme = parsed_url.scheme
|
79
94
|
self._is_localhost = False
|
80
95
|
|
81
96
|
if service not in ["blob", "queue", "file-share", "dfs"]:
|
82
97
|
raise ValueError(f"Invalid service: {service}")
|
83
|
-
service_name = service.split(
|
98
|
+
service_name = service.split("-")[0]
|
84
99
|
account = parsed_url.netloc.split(f".{service_name}.core.")
|
85
100
|
|
86
101
|
self.account_name = account[0] if len(account) > 1 else None
|
87
|
-
if
|
88
|
-
|
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
|
+
):
|
89
107
|
self._is_localhost = True
|
90
108
|
self.account_name = parsed_url.path.strip("/")
|
91
109
|
|
@@ -93,7 +111,7 @@ class StorageAccountHostsMixin(object):
|
|
93
111
|
if self.scheme.lower() != "https" and hasattr(self.credential, "get_token"):
|
94
112
|
raise ValueError("Token credential is only supported with HTTPS.")
|
95
113
|
|
96
|
-
secondary_hostname =
|
114
|
+
secondary_hostname = ""
|
97
115
|
if hasattr(self.credential, "account_name"):
|
98
116
|
self.account_name = self.credential.account_name
|
99
117
|
secondary_hostname = f"{self.credential.account_name}-secondary.{service_name}.{SERVICE_HOST_BASE}"
|
@@ -103,7 +121,7 @@ class StorageAccountHostsMixin(object):
|
|
103
121
|
secondary_hostname = parsed_url.netloc.replace(account[0], account[0] + "-secondary")
|
104
122
|
if kwargs.get("secondary_hostname"):
|
105
123
|
secondary_hostname = kwargs["secondary_hostname"]
|
106
|
-
primary_hostname = (parsed_url.netloc + parsed_url.path).rstrip(
|
124
|
+
primary_hostname = (parsed_url.netloc + parsed_url.path).rstrip("/")
|
107
125
|
self._hosts = {LocationMode.PRIMARY: primary_hostname, LocationMode.SECONDARY: secondary_hostname}
|
108
126
|
|
109
127
|
self._sdk_moniker = f"storage-{service}/{VERSION}"
|
@@ -116,70 +134,76 @@ class StorageAccountHostsMixin(object):
|
|
116
134
|
def __exit__(self, *args):
|
117
135
|
self._client.__exit__(*args)
|
118
136
|
|
119
|
-
def close(self):
|
120
|
-
"""
|
137
|
+
def close(self) -> None:
|
138
|
+
"""This method is to close the sockets opened by the client.
|
121
139
|
It need not be used when using with a context manager.
|
122
140
|
"""
|
123
141
|
self._client.close()
|
124
142
|
|
125
143
|
@property
|
126
|
-
def url(self):
|
144
|
+
def url(self) -> str:
|
127
145
|
"""The full endpoint URL to this entity, including SAS token if used.
|
128
146
|
|
129
147
|
This could be either the primary endpoint,
|
130
148
|
or the secondary endpoint depending on the current :func:`location_mode`.
|
131
|
-
|
149
|
+
|
150
|
+
:return: The full endpoint URL to this entity, including SAS token if used.
|
132
151
|
:rtype: str
|
133
152
|
"""
|
134
|
-
return self._format_url(self._hosts[self._location_mode])
|
153
|
+
return self._format_url(self._hosts[self._location_mode]) # type: ignore
|
135
154
|
|
136
155
|
@property
|
137
|
-
def primary_endpoint(self):
|
156
|
+
def primary_endpoint(self) -> str:
|
138
157
|
"""The full primary endpoint URL.
|
139
158
|
|
159
|
+
:return: The full primary endpoint URL.
|
140
160
|
:rtype: str
|
141
161
|
"""
|
142
|
-
return self._format_url(self._hosts[LocationMode.PRIMARY])
|
162
|
+
return self._format_url(self._hosts[LocationMode.PRIMARY]) # type: ignore
|
143
163
|
|
144
164
|
@property
|
145
|
-
def primary_hostname(self):
|
165
|
+
def primary_hostname(self) -> str:
|
146
166
|
"""The hostname of the primary endpoint.
|
147
167
|
|
168
|
+
:return: The hostname of the primary endpoint.
|
148
169
|
:rtype: str
|
149
170
|
"""
|
150
171
|
return self._hosts[LocationMode.PRIMARY]
|
151
172
|
|
152
173
|
@property
|
153
|
-
def secondary_endpoint(self):
|
174
|
+
def secondary_endpoint(self) -> str:
|
154
175
|
"""The full secondary endpoint URL if configured.
|
155
176
|
|
156
177
|
If not available a ValueError will be raised. To explicitly specify a secondary hostname, use the optional
|
157
178
|
`secondary_hostname` keyword argument on instantiation.
|
158
179
|
|
180
|
+
:return: The full secondary endpoint URL.
|
159
181
|
:rtype: str
|
160
|
-
:raise ValueError:
|
182
|
+
:raise ValueError: If no secondary endpoint is configured.
|
161
183
|
"""
|
162
184
|
if not self._hosts[LocationMode.SECONDARY]:
|
163
185
|
raise ValueError("No secondary host configured.")
|
164
|
-
return self._format_url(self._hosts[LocationMode.SECONDARY])
|
186
|
+
return self._format_url(self._hosts[LocationMode.SECONDARY]) # type: ignore
|
165
187
|
|
166
188
|
@property
|
167
|
-
def secondary_hostname(self):
|
189
|
+
def secondary_hostname(self) -> Optional[str]:
|
168
190
|
"""The hostname of the secondary endpoint.
|
169
191
|
|
170
192
|
If not available this will be None. To explicitly specify a secondary hostname, use the optional
|
171
193
|
`secondary_hostname` keyword argument on instantiation.
|
172
194
|
|
195
|
+
:return: The hostname of the secondary endpoint, or None if not configured.
|
173
196
|
:rtype: Optional[str]
|
174
197
|
"""
|
175
198
|
return self._hosts[LocationMode.SECONDARY]
|
176
199
|
|
177
200
|
@property
|
178
|
-
def location_mode(self):
|
201
|
+
def location_mode(self) -> str:
|
179
202
|
"""The location mode that the client is currently using.
|
180
203
|
|
181
204
|
By default this will be "primary". Options include "primary" and "secondary".
|
182
205
|
|
206
|
+
:return: The current location mode.
|
183
207
|
:rtype: str
|
184
208
|
"""
|
185
209
|
|
@@ -202,11 +226,16 @@ class StorageAccountHostsMixin(object):
|
|
202
226
|
return self._client._config.version # pylint: disable=protected-access
|
203
227
|
|
204
228
|
def _format_query_string(
|
205
|
-
self,
|
206
|
-
|
229
|
+
self,
|
230
|
+
sas_token: Optional[str],
|
231
|
+
credential: Optional[
|
232
|
+
Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", TokenCredential]
|
233
|
+
],
|
207
234
|
snapshot: Optional[str] = None,
|
208
|
-
share_snapshot: Optional[str] = None
|
209
|
-
) -> Tuple[
|
235
|
+
share_snapshot: Optional[str] = None,
|
236
|
+
) -> Tuple[
|
237
|
+
str, Optional[Union[str, Dict[str, str], "AzureNamedKeyCredential", "AzureSasCredential", TokenCredential]]
|
238
|
+
]:
|
210
239
|
query_str = "?"
|
211
240
|
if snapshot:
|
212
241
|
query_str += f"snapshot={snapshot}&"
|
@@ -214,7 +243,8 @@ class StorageAccountHostsMixin(object):
|
|
214
243
|
query_str += f"sharesnapshot={share_snapshot}&"
|
215
244
|
if sas_token and isinstance(credential, AzureSasCredential):
|
216
245
|
raise ValueError(
|
217
|
-
"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
|
+
)
|
218
248
|
if _is_credential_sastoken(credential):
|
219
249
|
credential = cast(str, credential)
|
220
250
|
query_str += credential.lstrip("?")
|
@@ -224,13 +254,16 @@ class StorageAccountHostsMixin(object):
|
|
224
254
|
return query_str.rstrip("?&"), credential
|
225
255
|
|
226
256
|
def _create_pipeline(
|
227
|
-
self,
|
228
|
-
|
257
|
+
self,
|
258
|
+
credential: Optional[
|
259
|
+
Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, TokenCredential]
|
260
|
+
] = None,
|
261
|
+
**kwargs: Any,
|
229
262
|
) -> Tuple[StorageConfiguration, Pipeline]:
|
230
263
|
self._credential_policy: Any = None
|
231
264
|
if hasattr(credential, "get_token"):
|
232
|
-
if kwargs.get(
|
233
|
-
audience = str(kwargs.pop(
|
265
|
+
if kwargs.get("audience"):
|
266
|
+
audience = str(kwargs.pop("audience")).rstrip("/") + DEFAULT_OAUTH_SCOPE
|
234
267
|
else:
|
235
268
|
audience = STORAGE_OAUTH_SCOPE
|
236
269
|
self._credential_policy = StorageBearerTokenCredentialPolicy(cast(TokenCredential, credential), audience)
|
@@ -264,22 +297,18 @@ class StorageAccountHostsMixin(object):
|
|
264
297
|
config.logging_policy,
|
265
298
|
StorageResponseHook(**kwargs),
|
266
299
|
DistributedTracingPolicy(**kwargs),
|
267
|
-
HttpLoggingPolicy(**kwargs)
|
300
|
+
HttpLoggingPolicy(**kwargs),
|
268
301
|
]
|
269
302
|
if kwargs.get("_additional_pipeline_policies"):
|
270
303
|
policies = policies + kwargs.get("_additional_pipeline_policies") # type: ignore
|
271
304
|
config.transport = transport # type: ignore
|
272
305
|
return config, Pipeline(transport, policies=policies)
|
273
306
|
|
274
|
-
def _batch_send(
|
275
|
-
self,
|
276
|
-
*reqs: "HttpRequest",
|
277
|
-
**kwargs: Any
|
278
|
-
) -> Iterator["HttpResponse"]:
|
307
|
+
def _batch_send(self, *reqs: "HttpRequest", **kwargs: Any) -> Iterator["HttpResponse"]:
|
279
308
|
"""Given a series of request, do a Storage batch call.
|
280
309
|
|
281
310
|
:param HttpRequest reqs: A collection of HttpRequest objects.
|
282
|
-
:
|
311
|
+
:return: An iterator of HttpResponse objects.
|
283
312
|
:rtype: Iterator[HttpResponse]
|
284
313
|
"""
|
285
314
|
# Pop it here, so requests doesn't feel bad about additional kwarg
|
@@ -288,25 +317,21 @@ class StorageAccountHostsMixin(object):
|
|
288
317
|
|
289
318
|
request = self._client._client.post( # pylint: disable=protected-access
|
290
319
|
url=(
|
291
|
-
f
|
320
|
+
f"{self.scheme}://{self.primary_hostname}/"
|
292
321
|
f"{kwargs.pop('path', '')}?{kwargs.pop('restype', '')}"
|
293
322
|
f"comp=batch{kwargs.pop('sas', '')}{kwargs.pop('timeout', '')}"
|
294
323
|
),
|
295
324
|
headers={
|
296
|
-
|
297
|
-
"Content-Type": "multipart/mixed; boundary=" + _get_batch_request_delimiter(batch_id, False, False)
|
298
|
-
}
|
325
|
+
"x-ms-version": self.api_version,
|
326
|
+
"Content-Type": "multipart/mixed; boundary=" + _get_batch_request_delimiter(batch_id, False, False),
|
327
|
+
},
|
299
328
|
)
|
300
329
|
|
301
330
|
policies = [StorageHeadersPolicy()]
|
302
331
|
if self._credential_policy:
|
303
332
|
policies.append(self._credential_policy)
|
304
333
|
|
305
|
-
request.set_multipart_mixed(
|
306
|
-
*reqs,
|
307
|
-
policies=policies,
|
308
|
-
enforce_https=False
|
309
|
-
)
|
334
|
+
request.set_multipart_mixed(*reqs, policies=policies, enforce_https=False)
|
310
335
|
|
311
336
|
Pipeline._prepare_multipart_mixed_request(request) # pylint: disable=protected-access
|
312
337
|
body = serialize_batch_body(request.multipart_mixed_info[0], batch_id)
|
@@ -314,9 +339,7 @@ class StorageAccountHostsMixin(object):
|
|
314
339
|
|
315
340
|
temp = request.multipart_mixed_info
|
316
341
|
request.multipart_mixed_info = None
|
317
|
-
pipeline_response = self._pipeline.run(
|
318
|
-
request, **kwargs
|
319
|
-
)
|
342
|
+
pipeline_response = self._pipeline.run(request, **kwargs)
|
320
343
|
response = pipeline_response.http_response
|
321
344
|
request.multipart_mixed_info = temp
|
322
345
|
|
@@ -328,8 +351,7 @@ class StorageAccountHostsMixin(object):
|
|
328
351
|
parts = list(response.parts())
|
329
352
|
if any(p for p in parts if not 200 <= p.status_code < 300):
|
330
353
|
error = PartialBatchErrorException(
|
331
|
-
message="There is a partial failure in the batch operation.",
|
332
|
-
response=response, parts=parts
|
354
|
+
message="There is a partial failure in the batch operation.", response=response, parts=parts
|
333
355
|
)
|
334
356
|
raise error
|
335
357
|
return iter(parts)
|
@@ -343,6 +365,7 @@ class TransportWrapper(HttpTransport):
|
|
343
365
|
by a `get_client` method does not close the outer transport for the parent
|
344
366
|
when used in a context manager.
|
345
367
|
"""
|
368
|
+
|
346
369
|
def __init__(self, transport):
|
347
370
|
self._transport = transport
|
348
371
|
|
@@ -364,7 +387,9 @@ class TransportWrapper(HttpTransport):
|
|
364
387
|
|
365
388
|
def _format_shared_key_credential(
|
366
389
|
account_name: Optional[str],
|
367
|
-
credential: Optional[
|
390
|
+
credential: Optional[
|
391
|
+
Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, "AsyncTokenCredential", TokenCredential]
|
392
|
+
] = None,
|
368
393
|
) -> Any:
|
369
394
|
if isinstance(credential, str):
|
370
395
|
if not account_name:
|
@@ -384,8 +409,12 @@ def _format_shared_key_credential(
|
|
384
409
|
def parse_connection_str(
|
385
410
|
conn_str: str,
|
386
411
|
credential: Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, TokenCredential]],
|
387
|
-
service: str
|
388
|
-
) -> Tuple[
|
412
|
+
service: str,
|
413
|
+
) -> Tuple[
|
414
|
+
str,
|
415
|
+
Optional[str],
|
416
|
+
Optional[Union[str, Dict[str, str], AzureNamedKeyCredential, AzureSasCredential, TokenCredential]],
|
417
|
+
]:
|
389
418
|
conn_str = conn_str.rstrip(";")
|
390
419
|
conn_settings_list = [s.split("=", 1) for s in conn_str.split(";")]
|
391
420
|
if any(len(tup) != 2 for tup in conn_settings_list):
|
@@ -407,14 +436,11 @@ def parse_connection_str(
|
|
407
436
|
if endpoints["secondary"] in conn_settings:
|
408
437
|
raise ValueError("Connection string specifies only secondary endpoint.")
|
409
438
|
try:
|
410
|
-
primary =(
|
439
|
+
primary = (
|
411
440
|
f"{conn_settings['DEFAULTENDPOINTSPROTOCOL']}://"
|
412
441
|
f"{conn_settings['ACCOUNTNAME']}.{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
413
442
|
)
|
414
|
-
secondary =
|
415
|
-
f"{conn_settings['ACCOUNTNAME']}-secondary."
|
416
|
-
f"{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
417
|
-
)
|
443
|
+
secondary = f"{conn_settings['ACCOUNTNAME']}-secondary." f"{service}.{conn_settings['ENDPOINTSUFFIX']}"
|
418
444
|
except KeyError:
|
419
445
|
pass
|
420
446
|
|
@@ -434,7 +460,7 @@ def parse_connection_str(
|
|
434
460
|
|
435
461
|
|
436
462
|
def create_configuration(**kwargs: Any) -> StorageConfiguration:
|
437
|
-
|
463
|
+
# Backwards compatibility if someone is not passing sdk_moniker
|
438
464
|
if not kwargs.get("sdk_moniker"):
|
439
465
|
kwargs["sdk_moniker"] = f"storage-{kwargs.pop('storage_sdk')}/{VERSION}"
|
440
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
|
|