Skip to content

Commit 0f9eb9c

Browse files
authored
[Key Vault] Remove direct uses of asyncio.sleep (#33819)
1 parent 665b921 commit 0f9eb9c

File tree

19 files changed

+300
-121
lines changed

19 files changed

+300
-121
lines changed

sdk/keyvault/azure-keyvault-certificates/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
### Other Changes
1212
- Python 3.7 is no longer supported. Please use Python version 3.8 or later.
13+
- `asyncio` is no longer directly referenced by the library
14+
([#33819](https://github.com/Azure/azure-sdk-for-python/pull/33819))
1315

1416
## 4.8.0b3 (2023-11-03)
1517

sdk/keyvault/azure-keyvault-certificates/azure/keyvault/certificates/_client.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,12 @@ def begin_create_certificate(
110110
tags=kwargs.pop("tags", None),
111111
)
112112

113-
cert_bundle = self._client.create_certificate(
114-
vault_base_url=self.vault_url, certificate_name=certificate_name, parameters=parameters, **kwargs
113+
pipeline_response, cert_bundle = self._client.create_certificate(
114+
vault_base_url=self.vault_url,
115+
certificate_name=certificate_name,
116+
parameters=parameters,
117+
cls=lambda pipeline_response, deserialized, _: (pipeline_response, deserialized),
118+
**kwargs,
115119
)
116120

117121
create_certificate_operation = CertificateOperation._from_certificate_operation_bundle(cert_bundle)
@@ -121,7 +125,9 @@ def begin_create_certificate(
121125
get_certificate_command = partial(self.get_certificate, certificate_name=certificate_name, **kwargs)
122126

123127
create_certificate_polling = CreateCertificatePoller(
124-
get_certificate_command=get_certificate_command, interval=polling_interval
128+
pipeline_response=pipeline_response,
129+
get_certificate_command=get_certificate_command,
130+
interval=polling_interval
125131
)
126132

127133
def no_op(*_, **__) -> Any: # The deserialization callback is ignored based on polling implementation
@@ -217,14 +223,18 @@ def begin_delete_certificate(self, certificate_name: str, **kwargs: Any) -> LROP
217223
polling_interval = kwargs.pop("_polling_interval", None)
218224
if polling_interval is None:
219225
polling_interval = 2
220-
deleted_cert_bundle = self._client.delete_certificate(
221-
vault_base_url=self.vault_url, certificate_name=certificate_name, **kwargs
226+
pipeline_response, deleted_cert_bundle = self._client.delete_certificate(
227+
vault_base_url=self.vault_url,
228+
certificate_name=certificate_name,
229+
cls=lambda pipeline_response, deserialized, _: (pipeline_response, deserialized),
230+
**kwargs,
222231
)
223232
deleted_cert = DeletedCertificate._from_deleted_certificate_bundle(deleted_cert_bundle)
224233

225234
polling_method = DeleteRecoverPollingMethod(
226235
# no recovery ID means soft-delete is disabled, in which case we initialize the poller as finished
227236
finished=deleted_cert.recovery_id is None,
237+
pipeline_response=pipeline_response,
228238
command=partial(self.get_deleted_certificate, certificate_name=certificate_name, **kwargs),
229239
final_resource=deleted_cert,
230240
interval=polling_interval,
@@ -312,13 +322,20 @@ def begin_recover_deleted_certificate(self, certificate_name: str, **kwargs: Any
312322
if polling_interval is None:
313323
polling_interval = 2
314324

315-
recovered_cert_bundle = self._client.recover_deleted_certificate(
316-
vault_base_url=self.vault_url, certificate_name=certificate_name, **kwargs
325+
pipeline_response, recovered_cert_bundle = self._client.recover_deleted_certificate(
326+
vault_base_url=self.vault_url,
327+
certificate_name=certificate_name,
328+
cls=lambda pipeline_response, deserialized, _: (pipeline_response, deserialized),
329+
**kwargs,
317330
)
318331
recovered_certificate = KeyVaultCertificate._from_certificate_bundle(recovered_cert_bundle)
319332
command = partial(self.get_certificate, certificate_name=certificate_name, **kwargs)
320333
polling_method = DeleteRecoverPollingMethod(
321-
finished=False, command=command, final_resource=recovered_certificate, interval=polling_interval
334+
finished=False,
335+
pipeline_response=pipeline_response,
336+
command=command,
337+
final_resource=recovered_certificate,
338+
interval=polling_interval
322339
)
323340

324341
return KeyVaultOperationPoller(polling_method)

sdk/keyvault/azure-keyvault-certificates/azure/keyvault/certificates/_polling.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55
import logging
6-
import time
7-
from typing import Any, Callable, Optional, Union
6+
from typing import Any, Callable, cast, Optional, Union
87

8+
from azure.core.pipeline import PipelineResponse
9+
from azure.core.pipeline.transport import HttpTransport
910
from azure.core.polling import PollingMethod
1011
from azure.keyvault.certificates._models import KeyVaultCertificate, CertificateOperation
1112

1213
logger = logging.getLogger(__name__)
1314

1415

1516
class CreateCertificatePoller(PollingMethod):
16-
def __init__(self, get_certificate_command: Callable, interval: int = 5) -> None:
17+
def __init__(
18+
self, pipeline_response: PipelineResponse, get_certificate_command: Callable, interval: int = 5
19+
) -> None:
20+
self._pipeline_response = pipeline_response
1721
self._command: Optional[Callable] = None
1822
self._resource: Optional[Union[CertificateOperation, KeyVaultCertificate]] = None
1923
self._pending_certificate_op: Optional[CertificateOperation] = None
@@ -32,7 +36,9 @@ def run(self) -> None:
3236
while not self.finished():
3337
self._update_status()
3438
if not self.finished():
35-
time.sleep(self._polling_interval)
39+
# We should always ask the client's transport to sleep, instead of sleeping directly
40+
transport: HttpTransport = cast(HttpTransport, self._pipeline_response.context.transport)
41+
transport.sleep(self._polling_interval)
3642
operation = self._pending_certificate_op
3743
if operation and operation.status and operation.status.lower() == "completed":
3844
self._resource = self._get_certificate_command()

sdk/keyvault/azure-keyvault-certificates/azure/keyvault/certificates/_shared/_polling.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55
import logging
6-
import time
76
import threading
87
import uuid
9-
from typing import TYPE_CHECKING
8+
from typing import Any, Callable, cast, Optional
109

11-
from azure.core.polling import PollingMethod, LROPoller, NoPolling
1210
from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
11+
from azure.core.pipeline import PipelineResponse
12+
from azure.core.pipeline.transport import HttpTransport
13+
from azure.core.polling import PollingMethod, LROPoller, NoPolling
1314

1415
from azure.core.tracing.decorator import distributed_trace
1516
from azure.core.tracing.common import with_current_context
1617

17-
if TYPE_CHECKING:
18-
# pylint: disable=ungrouped-imports
19-
from typing import Any, Callable, Optional
20-
2118
logger = logging.getLogger(__name__)
2219

2320

@@ -45,7 +42,7 @@ def result(self) -> "Any": # type: ignore
4542
return self._polling_method.resource()
4643

4744
@distributed_trace
48-
def wait(self, timeout: "Optional[float]" = None) -> None:
45+
def wait(self, timeout: Optional[float] = None) -> None:
4946
"""Wait on the long running operation for a number of seconds.
5047
5148
You can check if this call has ended with timeout with the "done()" method.
@@ -84,14 +81,24 @@ class DeleteRecoverPollingMethod(PollingMethod):
8481
Similarly, while recovering a deleted resource, Key Vault will respond 404 to GET requests for the non-deleted
8582
resource; when it responds 2xx, the resource exists in the non-deleted collection, i.e. its recovery is complete.
8683
84+
:param pipeline_response: The operation's original pipeline response.
85+
:type pipeline_response: PipelineResponse
8786
:param command: A callable to invoke when polling.
8887
:type command: Callable
8988
:param final_resource: The final resource returned by the polling operation.
9089
:type final_resource: Any
9190
:param bool finished: Whether or not the polling operation is completed.
9291
:param int interval: The polling interval, in seconds.
9392
"""
94-
def __init__(self, command: "Callable", final_resource: "Any", finished: bool, interval: int = 2) -> None:
93+
def __init__(
94+
self,
95+
pipeline_response: PipelineResponse,
96+
command: Callable,
97+
final_resource: Any,
98+
finished: bool,
99+
interval: int = 2
100+
) -> None:
101+
self._pipeline_response = pipeline_response
95102
self._command = command
96103
self._resource = final_resource
97104
self._polling_interval = interval
@@ -111,23 +118,25 @@ def _update_status(self) -> None:
111118
else:
112119
raise
113120

114-
def initialize(self, client: "Any", initial_response: "Any", deserialization_callback: "Callable") -> None:
121+
def initialize(self, client: Any, initial_response: Any, deserialization_callback: Callable) -> None:
115122
pass
116123

117124
def run(self) -> None:
118125
try:
119126
while not self.finished():
120127
self._update_status()
121128
if not self.finished():
122-
time.sleep(self._polling_interval)
129+
# We should always ask the client's transport to sleep, instead of sleeping directly
130+
transport: HttpTransport = cast(HttpTransport, self._pipeline_response.context.transport)
131+
transport.sleep(self._polling_interval)
123132
except Exception as e:
124133
logger.warning(str(e))
125134
raise
126135

127136
def finished(self) -> bool:
128137
return self._finished
129138

130-
def resource(self) -> "Any":
139+
def resource(self) -> Any:
131140
return self._resource
132141

133142
def status(self) -> str:

sdk/keyvault/azure-keyvault-certificates/azure/keyvault/certificates/_shared/_polling_async.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
# Copyright (c) Microsoft Corporation.
33
# Licensed under the MIT License.
44
# ------------------------------------
5-
6-
import asyncio
75
import logging
8-
from typing import Any, Callable
6+
from typing import Any, Callable, cast
97

10-
from azure.core.polling import AsyncPollingMethod
118
from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
9+
from azure.core.pipeline import PipelineResponse
10+
from azure.core.pipeline.transport import AsyncHttpTransport
11+
from azure.core.polling import AsyncPollingMethod
1212

1313
logger = logging.getLogger(__name__)
1414

@@ -24,6 +24,8 @@ class AsyncDeleteRecoverPollingMethod(AsyncPollingMethod):
2424
Similarly, while recovering a deleted resource, Key Vault will respond 404 to GET requests for the non-deleted
2525
resource; when it responds 2xx, the resource exists in the non-deleted collection, i.e. its recovery is complete.
2626
27+
:param pipeline_response: The operation's original pipeline response.
28+
:type pipeline_response: PipelineResponse
2729
:param command: An awaitable to invoke when polling.
2830
:type command: Callable
2931
:param final_resource: The final resource returned by the polling operation.
@@ -33,8 +35,14 @@ class AsyncDeleteRecoverPollingMethod(AsyncPollingMethod):
3335
"""
3436

3537
def __init__(
36-
self, command: Callable, final_resource: Any, finished: bool, interval: int = 2
38+
self,
39+
pipeline_response: PipelineResponse,
40+
command: Callable,
41+
final_resource: Any,
42+
finished: bool,
43+
interval: int = 2
3744
) -> None:
45+
self._pipeline_response = pipeline_response
3846
self._command = command
3947
self._resource = final_resource
4048
self._polling_interval = interval
@@ -62,7 +70,9 @@ async def run(self) -> None:
6270
while not self.finished():
6371
await self._update_status()
6472
if not self.finished():
65-
await asyncio.sleep(self._polling_interval)
73+
# We should always ask the client's transport to sleep, instead of sleeping directly
74+
transport: AsyncHttpTransport = cast(AsyncHttpTransport, self._pipeline_response.context.transport)
75+
await transport.sleep(self._polling_interval)
6676
except Exception as e:
6777
logger.warning(str(e))
6878
raise

sdk/keyvault/azure-keyvault-certificates/azure/keyvault/certificates/aio/_client.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import Any, Optional, List, Union
88
from functools import partial
99

10-
from azure.core.polling import async_poller
10+
from azure.core.polling import AsyncLROPoller
1111
from azure.core.tracing.decorator import distributed_trace
1212
from azure.core.tracing.decorator_async import distributed_trace_async
1313
from azure.core.async_paging import AsyncItemPaged
@@ -106,10 +106,11 @@ async def create_certificate(
106106
tags=kwargs.pop("tags", None),
107107
)
108108

109-
cert_bundle = await self._client.create_certificate(
109+
pipeline_response, cert_bundle = await self._client.create_certificate(
110110
vault_base_url=self.vault_url,
111111
certificate_name=certificate_name,
112112
parameters=parameters,
113+
cls=lambda pipeline_response, deserialized, _: (pipeline_response, deserialized),
113114
**kwargs
114115
)
115116

@@ -120,11 +121,13 @@ async def create_certificate(
120121
get_certificate_command = partial(self.get_certificate, certificate_name=certificate_name, **kwargs)
121122

122123
create_certificate_polling = CreateCertificatePollerAsync(
123-
get_certificate_command=get_certificate_command, interval=polling_interval
124+
pipeline_response=pipeline_response,
125+
get_certificate_command=get_certificate_command,
126+
interval=polling_interval,
124127
)
125128
def no_op(*_, **__) -> Any: # The deserialization callback is ignored based on polling implementation
126129
pass
127-
return await async_poller(command, create_certificate_operation, no_op, create_certificate_polling)
130+
return await AsyncLROPoller(command, create_certificate_operation, no_op, create_certificate_polling)
128131

129132
@distributed_trace_async
130133
async def get_certificate(self, certificate_name: str, **kwargs: Any) -> KeyVaultCertificate:
@@ -216,14 +219,18 @@ async def delete_certificate(self, certificate_name: str, **kwargs: Any) -> Dele
216219
polling_interval = kwargs.pop("_polling_interval", None)
217220
if polling_interval is None:
218221
polling_interval = 2
219-
deleted_cert_bundle = await self._client.delete_certificate(
220-
vault_base_url=self.vault_url, certificate_name=certificate_name, **kwargs
222+
pipeline_response, deleted_cert_bundle = await self._client.delete_certificate(
223+
vault_base_url=self.vault_url,
224+
certificate_name=certificate_name,
225+
cls=lambda pipeline_response, deserialized, _: (pipeline_response, deserialized),
226+
**kwargs,
221227
)
222228
deleted_certificate = DeletedCertificate._from_deleted_certificate_bundle(deleted_cert_bundle)
223229

224230
polling_method = AsyncDeleteRecoverPollingMethod(
225231
# no recovery ID means soft-delete is disabled, in which case we initialize the poller as finished
226232
finished=deleted_certificate.recovery_id is None,
233+
pipeline_response=pipeline_response,
227234
command=partial(self.get_deleted_certificate, certificate_name=certificate_name, **kwargs),
228235
final_resource=deleted_certificate,
229236
interval=polling_interval,
@@ -307,14 +314,21 @@ async def recover_deleted_certificate(self, certificate_name: str, **kwargs: Any
307314
polling_interval = kwargs.pop("_polling_interval", None)
308315
if polling_interval is None:
309316
polling_interval = 2
310-
recovered_cert_bundle = await self._client.recover_deleted_certificate(
311-
vault_base_url=self.vault_url, certificate_name=certificate_name, **kwargs
317+
pipeline_response, recovered_cert_bundle = await self._client.recover_deleted_certificate(
318+
vault_base_url=self.vault_url,
319+
certificate_name=certificate_name,
320+
cls=lambda pipeline_response, deserialized, _: (pipeline_response, deserialized),
321+
**kwargs,
312322
)
313323
recovered_certificate = KeyVaultCertificate._from_certificate_bundle(recovered_cert_bundle)
314324

315325
command = partial(self.get_certificate, certificate_name=certificate_name, **kwargs)
316326
polling_method = AsyncDeleteRecoverPollingMethod(
317-
command=command, final_resource=recovered_certificate, finished=False, interval=polling_interval
327+
pipeline_response=pipeline_response,
328+
command=command,
329+
final_resource=recovered_certificate,
330+
finished=False,
331+
interval=polling_interval
318332
)
319333
await polling_method.run()
320334

sdk/keyvault/azure-keyvault-certificates/azure/keyvault/certificates/aio/_polling_async.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,27 @@
22
# Copyright (c) Microsoft Corporation.
33
# Licensed under the MIT License.
44
# ------------------------------------
5-
6-
import asyncio
75
import logging
8-
from typing import Any, Callable, Optional, Union
6+
from typing import Any, Callable, cast, Optional, Union
7+
8+
from azure.core.pipeline import PipelineResponse
9+
from azure.core.pipeline.transport import AsyncHttpTransport
910
from azure.core.polling import AsyncPollingMethod
11+
1012
from .._models import KeyVaultCertificate, CertificateOperation
1113

1214

1315
logger = logging.getLogger(__name__)
1416

1517

1618
class CreateCertificatePollerAsync(AsyncPollingMethod):
17-
def __init__(self, get_certificate_command: Callable, interval: int = 5) -> None:
18-
self._command = None # type: Optional[Callable]
19-
self._resource = None # type: Optional[Union[CertificateOperation, KeyVaultCertificate]]
20-
self._pending_certificate_op = None # type: Optional[CertificateOperation]
19+
def __init__(
20+
self, pipeline_response: PipelineResponse, get_certificate_command: Callable, interval: int = 5
21+
) -> None:
22+
self._pipeline_response = pipeline_response
23+
self._command: Optional[Callable] = None
24+
self._resource: Optional[Union[CertificateOperation, KeyVaultCertificate]] = None
25+
self._pending_certificate_op: Optional[CertificateOperation] = None
2126
self._get_certificate_command = get_certificate_command
2227
self._polling_interval = interval
2328

@@ -33,7 +38,9 @@ async def run(self) -> None:
3338
while not self.finished():
3439
await self._update_status()
3540
if not self.finished():
36-
await asyncio.sleep(self._polling_interval)
41+
# We should always ask the client's transport to sleep, instead of sleeping directly
42+
transport: AsyncHttpTransport = cast(AsyncHttpTransport, self._pipeline_response.context.transport)
43+
await transport.sleep(self._polling_interval)
3744
operation = self._pending_certificate_op
3845
if operation and operation.status and operation.status.lower() == "completed":
3946
self._resource = await self._get_certificate_command()

sdk/keyvault/azure-keyvault-keys/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
### Other Changes
1212
- Python 3.7 is no longer supported. Please use Python version 3.8 or later.
13+
- `asyncio` is no longer directly referenced by the library
14+
([#33819](https://github.com/Azure/azure-sdk-for-python/pull/33819))
1315

1416
## 4.9.0b3 (2023-11-03)
1517

0 commit comments

Comments
 (0)