16
16
17
17
PollingReturnType_co = TypeVar ("PollingReturnType_co" , covariant = True )
18
18
19
- # The correct success response should be "Succeeded", but this has already shipped. Handle "Success " just in case.
19
+ # Correct success response should be "Succeeded", but this has already shipped. Still, handle "Succeeded " just in case.
20
20
_FINISHED = frozenset (["succeeded" , "success" , "canceled" , "failed" ])
21
21
22
22
@@ -38,6 +38,13 @@ def _is_empty(response: Union[HttpResponse, AsyncHttpResponse]) -> bool:
38
38
39
39
40
40
class PollingTerminationMixin (LROBasePolling ):
41
+ """Mixin to correctly handle polling termination.
42
+
43
+ Uses a custom implementation of `finished` because Security Domain LROs return "Success" as a terminal response
44
+ instead of the standard "Succeeded". At the time of writing, there's no way to more easily patch the base poller
45
+ from `azure-core` to handle this.
46
+ """
47
+
41
48
def finished (self ) -> bool :
42
49
"""Is this polling finished?
43
50
@@ -72,6 +79,12 @@ def parse_resource(
72
79
73
80
74
81
class NoPollingMixin (LROBasePolling ):
82
+ """Mixin that intentionally bypasses any polling by immediately returning a success status.
83
+
84
+ The Azure CLI accepts a `--no-wait` parameter in download and upload operations, allowing users to immediately get
85
+ the result before HSM activation completes. This polling logic is used to support that behavior.
86
+ """
87
+
75
88
def finished (self ) -> bool :
76
89
"""Is this polling finished?
77
90
@@ -93,6 +106,8 @@ def result(self, *args, **kwargs): # pylint: disable=unused-argument
93
106
94
107
95
108
class SecurityDomainDownloadPolling (OperationResourcePolling ):
109
+ """Adapts to the non-standard response pattern for security domain download."""
110
+
96
111
def __init__ (self ) -> None :
97
112
self ._polling_url = ""
98
113
super ().__init__ (operation_location_header = "azure-asyncoperation" )
@@ -101,9 +116,28 @@ def get_polling_url(self) -> str:
101
116
return self ._polling_url
102
117
103
118
def get_final_get_url (self , pipeline_response : "PipelineResponse" ) -> None :
119
+ """Returns None instead of a URL because the final result includes a status monitor but no resource URL.
120
+
121
+ :param pipeline_response: The response object. Unused here.
122
+ :type pipeline_response: ~azure.core.pipeline.PipelineResponse
123
+
124
+ :rtype: None
125
+ :return: None
126
+ """
104
127
return None
105
128
106
129
def set_initial_status (self , pipeline_response : "PipelineResponse" ) -> str :
130
+ """Manually marks the operation as "InProgress".
131
+
132
+ This is necessary because the initial response includes the security domain object -- which would usually be the
133
+ result fetched from a final resource URL -- but no status monitor.
134
+
135
+ :param pipeline_response: The response object.
136
+ :type pipeline_response: ~azure.core.pipeline.PipelineResponse
137
+
138
+ :rtype: str
139
+ :return: The initial status, which is always "InProgress".
140
+ """
107
141
response : HttpResponse = pipeline_response .http_response
108
142
self ._polling_url = response .headers ["azure-asyncoperation" ]
109
143
@@ -115,6 +149,8 @@ def set_initial_status(self, pipeline_response: "PipelineResponse") -> str:
115
149
116
150
117
151
class SecurityDomainDownloadPollingMethod (PollingTerminationMixin , LROBasePolling ):
152
+ """Polling method for the unique pattern of security domain download operations."""
153
+
118
154
def initialize (
119
155
self ,
120
156
client : PipelineClient [Any , Any ],
@@ -142,20 +178,32 @@ def get_long_running_output(pipeline_response):
142
178
super ().initialize (client , initial_response , get_long_running_output )
143
179
144
180
def resource (self ) -> SecurityDomain :
145
- """Return the built resource .
181
+ """Return the security domain deserialized from the initial response .
146
182
147
- :rtype: any
148
- :return: The built resource.
183
+ This returns the final result of the `SecurityDomainClient.begin_download` operation by deserializing the
184
+ initial response. This is an unusual LRO pattern and requires custom support. Usually, the object returned from
185
+ an LRO is only returned as part of the terminal status response; in Security Domain, the download operation
186
+ instead immediately returns the security domain object, and the terminal response only includes the activation
187
+ status.
188
+
189
+ :rtype: ~azure.keyvault.securitydomain.SecurityDomain
190
+ :return: The security domain object.
149
191
"""
150
192
# The final response should actually be the security domain object that was returned in the initial response
151
193
return cast (SecurityDomain , self .parse_resource (self ._initial_response ))
152
194
153
195
154
196
class SecurityDomainDownloadNoPolling (SecurityDomainDownloadPollingMethod , NoPollingMixin ):
155
- pass
197
+ """Polling method for security domain download operations that bypass polling."""
156
198
157
199
158
200
class SecurityDomainUploadPolling (SecurityDomainDownloadPolling ):
201
+ """Polling logic for security domain upload operations.
202
+
203
+ This class inherits from `SecurityDomainDownloadPolling` but uses the actual initial response status since the
204
+ upload operation has a more typical LRO resource pattern.
205
+ """
206
+
159
207
def set_initial_status (self , pipeline_response : PipelineResponse ) -> str :
160
208
response : HttpResponse = pipeline_response .http_response
161
209
self ._polling_url = response .headers ["azure-asyncoperation" ]
@@ -166,6 +214,13 @@ def set_initial_status(self, pipeline_response: PipelineResponse) -> str:
166
214
167
215
168
216
class SecurityDomainUploadPollingMethod (PollingTerminationMixin , LROBasePolling ):
217
+ """Polling method that will poll the HSM's activation but returns None.
218
+
219
+ This is manually done because the generated implementation returns a poller with a status monitor for a final
220
+ result. Python guidelines suggest returning None instead in this scenario, since the polling status can already be
221
+ accessed from the poller object.
222
+ """
223
+
169
224
def initialize (
170
225
self ,
171
226
client : PipelineClient [Any , Any ],
@@ -192,12 +247,13 @@ def get_long_running_output(_):
192
247
super ().initialize (client , initial_response , get_long_running_output )
193
248
194
249
def resource (self ) -> None :
195
- """Return the built resource.
250
+ """Return the final resource -- in this case, None .
196
251
197
- :rtype: any
198
- :return: The built resource.
252
+ :rtype: None
253
+ :return: The final resource -- in this case, None .
199
254
"""
200
255
return None
201
256
257
+
202
258
class SecurityDomainUploadNoPolling (SecurityDomainUploadPollingMethod , NoPollingMixin ):
203
- pass
259
+ """Polling method for security domain upload operations that bypass polling."""
0 commit comments