or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconfiguration.mdexceptions.mdindex.mdpaging.mdpipeline.mdpolling.mdserialization.mdservice-client.md

polling.mddocs/

0

# Long-Running Operations

1

2

Polling system for long-running operations with configurable retry strategies, timeout handling, and completion detection for Azure ARM and other async API patterns. The LRO (Long-Running Operation) system handles operations that don't complete immediately.

3

4

## Capabilities

5

6

### LRO Poller

7

8

Main class for polling long-running operations until completion.

9

10

```python { .api }

11

class LROPoller:

12

def result(self, timeout=None):

13

"""

14

Get the result of the long-running operation.

15

16

Parameters:

17

- timeout: Maximum time to wait in seconds (None for no timeout)

18

19

Returns:

20

Final result of the operation

21

22

Raises:

23

TimeoutError if operation doesn't complete within timeout

24

"""

25

26

def wait(self, timeout=None):

27

"""

28

Wait for operation to complete without returning result.

29

30

Parameters:

31

- timeout: Maximum time to wait in seconds

32

"""

33

34

def done(self) -> bool:

35

"""

36

Check if operation is complete.

37

38

Returns:

39

True if operation is finished, False otherwise

40

"""

41

42

def add_done_callback(self, func):

43

"""

44

Add callback to execute when operation completes.

45

46

Parameters:

47

- func: Callback function to execute

48

"""

49

50

def remove_done_callback(self, func):

51

"""

52

Remove previously added callback.

53

54

Parameters:

55

- func: Callback function to remove

56

"""

57

58

def status(self) -> str:

59

"""

60

Get current operation status.

61

62

Returns:

63

Status string (e.g., 'InProgress', 'Succeeded', 'Failed')

64

"""

65

```

66

67

### Polling Methods

68

69

Base class and implementations for different polling strategies.

70

71

```python { .api }

72

class PollingMethod:

73

def initialize(self, client, initial_response, deserialization_callback):

74

"""

75

Initialize polling method with initial response.

76

77

Parameters:

78

- client: HTTP client for making requests

79

- initial_response: Initial operation response

80

- deserialization_callback: Function to deserialize responses

81

"""

82

83

def run(self) -> bool:

84

"""

85

Execute one polling iteration.

86

87

Returns:

88

True if operation is complete, False to continue polling

89

"""

90

91

def status(self) -> str:

92

"""Get current operation status."""

93

94

def finished(self) -> bool:

95

"""Check if operation is finished."""

96

97

def resource(self):

98

"""Get final resource/result."""

99

100

class NoPolling(PollingMethod):

101

"""

102

No-op polling method that considers operation immediately complete.

103

Used for operations that complete synchronously.

104

"""

105

```

106

107

### Async Polling (Python 3.5+)

108

109

Async versions of polling classes for asynchronous operations.

110

111

```python { .api }

112

class AsyncPollingMethod:

113

async def run(self) -> bool:

114

"""Execute one polling iteration asynchronously."""

115

116

def initialize(self, client, initial_response, deserialization_callback):

117

"""Initialize async polling method."""

118

119

class AsyncNoPolling(AsyncPollingMethod):

120

"""Async no-op polling method."""

121

122

def async_poller(client, initial_response, deserialization_callback, polling_method):

123

"""

124

Create async poller for long-running operations.

125

126

Parameters:

127

- client: Async HTTP client

128

- initial_response: Initial operation response

129

- deserialization_callback: Response deserialization function

130

- polling_method: Async polling method instance

131

132

Returns:

133

Async poller object

134

"""

135

```

136

137

## Usage Examples

138

139

### Basic Long-Running Operation

140

141

```python

142

from msrest import ServiceClient, Configuration

143

from msrest.polling import LROPoller

144

145

# Setup client

146

config = Configuration(base_url='https://api.example.com')

147

client = ServiceClient(None, config)

148

149

# Start long-running operation

150

request = client.post('/start-operation', content='{"data": "value"}')

151

initial_response = client.send(request)

152

153

# Create poller (assuming service provides one)

154

# This is typically done by the SDK, not manually

155

poller = LROPoller(client, initial_response, deserialization_callback)

156

157

# Wait for completion and get result

158

try:

159

result = poller.result(timeout=300) # 5 minute timeout

160

print(f"Operation completed: {result}")

161

except TimeoutError:

162

print("Operation timed out")

163

```

164

165

### Polling with Status Checks

166

167

```python

168

from msrest.polling import LROPoller

169

import time

170

171

# Start operation and get poller

172

poller = start_long_running_operation()

173

174

# Poll manually with status checks

175

while not poller.done():

176

status = poller.status()

177

print(f"Operation status: {status}")

178

179

if status == 'Failed':

180

print("Operation failed")

181

break

182

elif status == 'Cancelled':

183

print("Operation was cancelled")

184

break

185

186

# Wait before next check

187

time.sleep(10)

188

189

if poller.done():

190

result = poller.result()

191

print(f"Final result: {result}")

192

```

193

194

### Using Callbacks

195

196

```python

197

from msrest.polling import LROPoller

198

199

def operation_completed(poller):

200

"""Callback function executed when operation completes."""

201

try:

202

result = poller.result()

203

print(f"Operation finished successfully: {result}")

204

except Exception as e:

205

print(f"Operation failed: {e}")

206

207

def operation_progress(poller):

208

"""Callback for progress updates."""

209

status = poller.status()

210

print(f"Progress update: {status}")

211

212

# Create poller and add callbacks

213

poller = start_long_running_operation()

214

poller.add_done_callback(operation_completed)

215

216

# Wait for completion (callbacks will be called)

217

poller.wait(timeout=600) # 10 minute timeout

218

```

219

220

### Custom Polling Method

221

222

```python

223

from msrest.polling import PollingMethod

224

import time

225

import json

226

227

class CustomPollingMethod(PollingMethod):

228

"""Custom polling method for specific API pattern."""

229

230

def __init__(self, timeout=30, interval=5):

231

super(CustomPollingMethod, self).__init__()

232

self.timeout = timeout

233

self.interval = interval

234

self._status = 'InProgress'

235

self._finished = False

236

self._resource = None

237

238

def initialize(self, client, initial_response, deserialization_callback):

239

"""Initialize with API-specific logic."""

240

self._client = client

241

self._deserialize = deserialization_callback

242

243

# Parse initial response for operation URL

244

response_data = json.loads(initial_response.text)

245

self._operation_url = response_data.get('operation_url')

246

self._status = response_data.get('status', 'InProgress')

247

248

def run(self):

249

"""Check operation status."""

250

if self._finished:

251

return True

252

253

# Make status check request

254

request = self._client.get(self._operation_url)

255

response = self._client.send(request)

256

257

# Parse response

258

data = json.loads(response.text)

259

self._status = data.get('status')

260

261

if self._status in ['Succeeded', 'Completed']:

262

self._finished = True

263

self._resource = self._deserialize('OperationResult', response)

264

return True

265

elif self._status in ['Failed', 'Cancelled']:

266

self._finished = True

267

return True

268

269

# Still in progress

270

time.sleep(self.interval)

271

return False

272

273

def status(self):

274

return self._status

275

276

def finished(self):

277

return self._finished

278

279

def resource(self):

280

return self._resource

281

282

# Use custom polling method

283

custom_method = CustomPollingMethod(timeout=300, interval=10)

284

poller = LROPoller(client, initial_response, custom_method)

285

286

result = poller.result()

287

```

288

289

### Async Long-Running Operations

290

291

```python

292

import asyncio

293

from msrest.polling import async_poller, AsyncNoPolling

294

295

async def async_long_running_operation():

296

"""Example async long-running operation."""

297

298

# Start async operation

299

async_client = await create_async_client()

300

initial_response = await async_client.post('/async-operation')

301

302

# Create async poller

303

polling_method = AsyncNoPolling() # or custom async method

304

poller = async_poller(

305

async_client,

306

initial_response,

307

deserialization_callback,

308

polling_method

309

)

310

311

# Wait for completion asynchronously

312

result = await poller.result(timeout=300)

313

return result

314

315

# Run async operation

316

async def main():

317

try:

318

result = await async_long_running_operation()

319

print(f"Async operation result: {result}")

320

except asyncio.TimeoutError:

321

print("Async operation timed out")

322

323

asyncio.run(main())

324

```

325

326

### Azure ARM Pattern Example

327

328

```python

329

from msrest.polling import LROPoller

330

import json

331

332

class AzureARMPollingMethod:

333

"""Polling method for Azure Resource Manager operations."""

334

335

def initialize(self, client, initial_response, deserialization_callback):

336

self._client = client

337

self._deserialize = deserialization_callback

338

339

# Azure ARM uses specific headers for polling

340

headers = initial_response.headers

341

342

# Check for Azure-AsyncOperation header (preferred)

343

if 'Azure-AsyncOperation' in headers:

344

self._polling_url = headers['Azure-AsyncOperation']

345

self._method = 'async_operation'

346

# Check for Location header (fallback)

347

elif 'Location' in headers:

348

self._polling_url = headers['Location']

349

self._method = 'location'

350

else:

351

# No polling needed, operation is complete

352

self._method = 'no_polling'

353

self._finished = True

354

return

355

356

self._finished = False

357

358

def run(self):

359

if self._finished:

360

return True

361

362

# Poll the appropriate URL

363

request = self._client.get(self._polling_url)

364

response = self._client.send(request)

365

366

if self._method == 'async_operation':

367

# Parse Azure-AsyncOperation response

368

data = json.loads(response.text)

369

status = data.get('status')

370

371

if status == 'Succeeded':

372

# Need to get final result from original Location

373

self._finished = True

374

return True

375

elif status in ['Failed', 'Cancelled']:

376

self._finished = True

377

return True

378

379

elif self._method == 'location':

380

# Location polling - check response status

381

if response.status_code == 200:

382

self._finished = True

383

return True

384

elif response.status_code == 202:

385

# Still in progress

386

return False

387

388

return False

389

390

# Use with Azure operations

391

def create_azure_resource():

392

# Start resource creation

393

request = client.put('/subscriptions/.../resourceGroups/.../providers/.../resource')

394

initial_response = client.send(request)

395

396

# Create poller with Azure-specific method

397

azure_method = AzureARMPollingMethod()

398

poller = LROPoller(client, initial_response, azure_method)

399

400

# Wait for resource creation to complete

401

result = poller.result(timeout=600) # 10 minutes

402

return result

403

```

404

405

### Error Handling with LRO

406

407

```python

408

from msrest.polling import LROPoller

409

from msrest.exceptions import HttpOperationError

410

411

def robust_long_running_operation():

412

"""LRO with comprehensive error handling."""

413

414

try:

415

# Start operation

416

poller = start_operation()

417

418

# Add error callback

419

def error_callback(poller):

420

status = poller.status()

421

if status == 'Failed':

422

print("Operation failed - checking details")

423

# Could inspect poller.result() for error details

424

425

poller.add_done_callback(error_callback)

426

427

# Wait with timeout

428

result = poller.result(timeout=300)

429

430

if poller.status() == 'Succeeded':

431

return result

432

else:

433

raise Exception(f"Operation ended with status: {poller.status()}")

434

435

except TimeoutError:

436

print("Operation timed out")

437

# Could cancel operation or continue waiting

438

raise

439

440

except HttpOperationError as e:

441

print(f"HTTP error during polling: {e.response.status_code}")

442

raise

443

444

except Exception as e:

445

print(f"Unexpected error during LRO: {e}")

446

raise

447

448

# Usage with error handling

449

try:

450

result = robust_long_running_operation()

451

print(f"Success: {result}")

452

except Exception as e:

453

print(f"LRO failed: {e}")

454

```

455

456

### Monitoring Multiple Operations

457

458

```python

459

from msrest.polling import LROPoller

460

import threading

461

import time

462

463

def monitor_multiple_operations():

464

"""Monitor multiple long-running operations concurrently."""

465

466

# Start multiple operations

467

pollers = []

468

for i in range(5):

469

poller = start_operation(f"operation_{i}")

470

pollers.append((f"operation_{i}", poller))

471

472

# Monitor all operations

473

completed = {}

474

475

while len(completed) < len(pollers):

476

for name, poller in pollers:

477

if name not in completed and poller.done():

478

try:

479

result = poller.result()

480

completed[name] = result

481

print(f"{name} completed successfully")

482

except Exception as e:

483

completed[name] = None

484

print(f"{name} failed: {e}")

485

486

# Check every 10 seconds

487

time.sleep(10)

488

489

return completed

490

491

# Run monitoring

492

results = monitor_multiple_operations()

493

print(f"All operations completed: {len(results)} total")

494

```

495

496

## Types

497

498

```python { .api }

499

# Common LRO status values

500

LRO_STATUS_IN_PROGRESS = "InProgress"

501

LRO_STATUS_SUCCEEDED = "Succeeded"

502

LRO_STATUS_FAILED = "Failed"

503

LRO_STATUS_CANCELLED = "Cancelled"

504

505

# Callback function signature

506

def lro_callback(poller: LROPoller) -> None:

507

"""

508

Callback function called when LRO completes.

509

510

Parameters:

511

- poller: The LROPoller instance that completed

512

"""

513

```