or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client.mdconfig.mdcredentials.mdevents.mdexceptions.mdindex.mdmodels.mdpagination.mdresponse.mdsession.mdtesting.mdwaiters.md

waiters.mddocs/

0

# Waiters

1

2

Resource state waiters that poll AWS services until resources reach desired states, with configurable polling intervals and timeout handling. Waiters provide a reliable mechanism to wait for asynchronous operations to complete, such as EC2 instances becoming available or DynamoDB tables reaching active status.

3

4

## Capabilities

5

6

### Waiter Access

7

8

Access waiters through AWS service clients to wait for resource state changes.

9

10

```python { .api }

11

class BaseClient:

12

def get_waiter(self, waiter_name: str) -> Waiter:

13

"""

14

Get a waiter instance for polling resource states.

15

16

Args:

17

waiter_name: Name of the waiter (e.g., 'instance_running', 'bucket_exists')

18

19

Returns:

20

Waiter: Configured waiter instance for the specified resource state

21

22

Raises:

23

ValueError: If waiter_name is not available for the service

24

"""

25

26

@property

27

def waiter_names(self) -> List[str]:

28

"""

29

List of available waiter names for this service.

30

31

Returns:

32

List[str]: Available waiter names

33

"""

34

```

35

36

### Core Waiter Class

37

38

Primary waiter implementation that handles polling and state transitions.

39

40

```python { .api }

41

class Waiter:

42

def __init__(

43

self,

44

name: str,

45

config: SingleWaiterConfig,

46

operation_method: callable

47

):

48

"""

49

Initialize a waiter instance.

50

51

Args:

52

name: Waiter name identifier

53

config: Waiter configuration and acceptor rules

54

operation_method: AWS operation method to call for polling

55

"""

56

57

@property

58

def name(self) -> str:

59

"""

60

Waiter name identifier.

61

62

Returns:

63

str: The name of this waiter

64

"""

65

66

@property

67

def config(self) -> SingleWaiterConfig:

68

"""

69

Waiter configuration including polling settings and acceptors.

70

71

Returns:

72

SingleWaiterConfig: Configuration object with delay, max_attempts, and acceptors

73

"""

74

75

def wait(self, **kwargs) -> None:

76

"""

77

Wait for the resource to reach the desired state.

78

79

Args:

80

**kwargs: Parameters to pass to the underlying AWS operation

81

WaiterConfig: Optional waiter configuration overrides

82

83

Raises:

84

WaiterError: If waiter times out, encounters failure state, or receives errors

85

"""

86

```

87

88

### Waiter Configuration

89

90

Configure waiter behavior with custom timing and retry settings.

91

92

```python { .api }

93

class WaiterModel:

94

SUPPORTED_VERSION: int = 2

95

96

def __init__(self, waiter_config: dict):

97

"""

98

Initialize waiter model from service configuration.

99

100

Args:

101

waiter_config: Loaded waiter configuration from service model

102

103

Raises:

104

WaiterConfigError: If waiter version is unsupported

105

"""

106

107

@property

108

def version(self) -> int:

109

"""

110

Waiter configuration format version.

111

112

Returns:

113

int: Configuration version (always 2 for supported models)

114

"""

115

116

@property

117

def waiter_names(self) -> List[str]:

118

"""

119

Available waiter names in this model.

120

121

Returns:

122

List[str]: Sorted list of waiter names

123

"""

124

125

def get_waiter(self, waiter_name: str) -> SingleWaiterConfig:

126

"""

127

Get configuration for a specific waiter.

128

129

Args:

130

waiter_name: Name of the waiter to retrieve

131

132

Returns:

133

SingleWaiterConfig: Waiter configuration object

134

135

Raises:

136

ValueError: If waiter_name does not exist

137

"""

138

139

class SingleWaiterConfig:

140

def __init__(self, single_waiter_config: dict):

141

"""

142

Initialize single waiter configuration.

143

144

Args:

145

single_waiter_config: Configuration dictionary for one waiter

146

"""

147

148

@property

149

def description(self) -> str:

150

"""

151

Human-readable waiter description.

152

153

Returns:

154

str: Waiter description text

155

"""

156

157

@property

158

def operation(self) -> str:

159

"""

160

AWS operation name used for polling.

161

162

Returns:

163

str: Operation name (e.g., 'DescribeInstances', 'HeadBucket')

164

"""

165

166

@property

167

def delay(self) -> int:

168

"""

169

Default delay between polling attempts in seconds.

170

171

Returns:

172

int: Delay in seconds

173

"""

174

175

@property

176

def max_attempts(self) -> int:

177

"""

178

Maximum number of polling attempts before timeout.

179

180

Returns:

181

int: Maximum attempts count

182

"""

183

184

@property

185

def acceptors(self) -> List[AcceptorConfig]:

186

"""

187

List of acceptor rules that determine state transitions.

188

189

Returns:

190

List[AcceptorConfig]: Acceptor configurations

191

"""

192

193

class AcceptorConfig:

194

def __init__(self, config: dict):

195

"""

196

Initialize acceptor configuration.

197

198

Args:

199

config: Acceptor configuration dictionary

200

"""

201

202

@property

203

def state(self) -> str:

204

"""

205

Target state for this acceptor ('success', 'failure', 'retry').

206

207

Returns:

208

str: State transition target

209

"""

210

211

@property

212

def matcher(self) -> str:

213

"""

214

Matcher type ('path', 'pathAll', 'pathAny', 'status', 'error').

215

216

Returns:

217

str: Matcher type identifier

218

"""

219

220

@property

221

def expected(self) -> Any:

222

"""

223

Expected value for successful match.

224

225

Returns:

226

Any: Expected value (string, int, bool, etc.)

227

"""

228

229

@property

230

def argument(self) -> str:

231

"""

232

JMESPath expression or matcher argument.

233

234

Returns:

235

str: JMESPath expression for path matchers

236

"""

237

238

@property

239

def explanation(self) -> str:

240

"""

241

Human-readable explanation of what this acceptor matches.

242

243

Returns:

244

str: Explanation text for debugging

245

"""

246

247

@property

248

def matcher_func(self) -> callable:

249

"""

250

Compiled matcher function that evaluates responses.

251

252

Returns:

253

callable: Function that takes response dict and returns bool

254

"""

255

```

256

257

## Usage Examples

258

259

### Basic Waiter Usage

260

261

Wait for AWS resources to reach desired states using service-specific waiters.

262

263

```python

264

from botocore.session import get_session

265

266

# Create session and clients

267

session = get_session()

268

ec2_client = session.create_client('ec2', region_name='us-east-1')

269

s3_client = session.create_client('s3', region_name='us-east-1')

270

271

# Wait for EC2 instance to be running

272

instance_id = 'i-1234567890abcdef0'

273

waiter = ec2_client.get_waiter('instance_running')

274

waiter.wait(InstanceIds=[instance_id])

275

print(f"Instance {instance_id} is now running")

276

277

# Wait for S3 bucket to exist

278

bucket_name = 'my-example-bucket'

279

waiter = s3_client.get_waiter('bucket_exists')

280

waiter.wait(Bucket=bucket_name)

281

print(f"Bucket {bucket_name} exists and is accessible")

282

```

283

284

### Custom Waiter Configuration

285

286

Override default polling behavior with custom timing settings.

287

288

```python

289

# Wait with custom configuration

290

waiter = ec2_client.get_waiter('instance_stopped')

291

waiter.wait(

292

InstanceIds=['i-1234567890abcdef0'],

293

WaiterConfig={

294

'Delay': 10, # Poll every 10 seconds instead of default

295

'MaxAttempts': 60 # Try up to 60 times instead of default

296

}

297

)

298

```

299

300

### DynamoDB Table Waiters

301

302

Wait for DynamoDB table operations to complete.

303

304

```python

305

dynamodb_client = session.create_client('dynamodb', region_name='us-east-1')

306

307

# Create table and wait for it to become active

308

dynamodb_client.create_table(

309

TableName='MyTable',

310

KeySchema=[

311

{'AttributeName': 'id', 'KeyType': 'HASH'}

312

],

313

AttributeDefinitions=[

314

{'AttributeName': 'id', 'AttributeType': 'S'}

315

],

316

BillingMode='PAY_PER_REQUEST'

317

)

318

319

# Wait for table to be ready

320

waiter = dynamodb_client.get_waiter('table_exists')

321

waiter.wait(TableName='MyTable')

322

print("Table is now active and ready to use")

323

324

# Delete table and wait for deletion to complete

325

dynamodb_client.delete_table(TableName='MyTable')

326

waiter = dynamodb_client.get_waiter('table_not_exists')

327

waiter.wait(TableName='MyTable')

328

print("Table has been successfully deleted")

329

```

330

331

### CloudFormation Stack Waiters

332

333

Monitor CloudFormation stack operations until completion.

334

335

```python

336

cloudformation_client = session.create_client('cloudformation', region_name='us-east-1')

337

338

# Create stack and wait for completion

339

cloudformation_client.create_stack(

340

StackName='my-stack',

341

TemplateBody=template_content,

342

Parameters=[

343

{'ParameterKey': 'Environment', 'ParameterValue': 'production'}

344

]

345

)

346

347

# Wait for stack creation to complete

348

waiter = cloudformation_client.get_waiter('stack_create_complete')

349

waiter.wait(StackName='my-stack')

350

print("Stack creation completed successfully")

351

352

# Update stack and wait for update completion

353

cloudformation_client.update_stack(

354

StackName='my-stack',

355

TemplateBody=updated_template_content

356

)

357

358

waiter = cloudformation_client.get_waiter('stack_update_complete')

359

waiter.wait(StackName='my-stack')

360

print("Stack update completed successfully")

361

```

362

363

### Discovering Available Waiters

364

365

Find out which waiters are available for a service.

366

367

```python

368

# List all available waiters for a service

369

ec2_waiters = ec2_client.waiter_names

370

print(f"EC2 waiters: {ec2_waiters}")

371

372

s3_waiters = s3_client.waiter_names

373

print(f"S3 waiters: {s3_waiters}")

374

375

# Check if specific waiter exists

376

if 'instance_running' in ec2_client.waiter_names:

377

waiter = ec2_client.get_waiter('instance_running')

378

print(f"Waiter config: delay={waiter.config.delay}s, max_attempts={waiter.config.max_attempts}")

379

```

380

381

### Error Handling

382

383

Handle waiter timeouts and failure conditions properly.

384

385

```python

386

from botocore.exceptions import WaiterError

387

388

try:

389

waiter = ec2_client.get_waiter('instance_running')

390

waiter.wait(

391

InstanceIds=['i-1234567890abcdef0'],

392

WaiterConfig={'MaxAttempts': 10}

393

)

394

except WaiterError as e:

395

print(f"Waiter failed: {e.reason}")

396

print(f"Last response: {e.last_response}")

397

398

# Handle specific failure cases

399

if 'Max attempts exceeded' in e.reason:

400

print("Instance took too long to start")

401

elif 'terminal failure state' in e.reason:

402

print("Instance failed to start properly")

403

```

404

405

### Lambda Function Waiters

406

407

Wait for Lambda function states and configurations.

408

409

```python

410

lambda_client = session.create_client('lambda', region_name='us-east-1')

411

412

# Wait for function to be active after creation

413

waiter = lambda_client.get_waiter('function_active')

414

waiter.wait(FunctionName='my-function')

415

print("Lambda function is active")

416

417

# Wait for function configuration update to complete

418

lambda_client.update_function_configuration(

419

FunctionName='my-function',

420

Runtime='python3.9',

421

Handler='lambda_function.lambda_handler'

422

)

423

424

waiter = lambda_client.get_waiter('function_updated')

425

waiter.wait(FunctionName='my-function')

426

print("Function configuration update completed")

427

```

428

429

## Common Waiter Types

430

431

### EC2 Instance Waiters

432

433

- `instance_exists`: Instance appears in describe_instances

434

- `instance_running`: Instance reaches 'running' state

435

- `instance_stopped`: Instance reaches 'stopped' state

436

- `instance_terminated`: Instance reaches 'terminated' state

437

- `instance_status_ok`: Instance passes status checks

438

439

### S3 Bucket Waiters

440

441

- `bucket_exists`: Bucket is accessible via HEAD request

442

- `bucket_not_exists`: Bucket no longer exists or is inaccessible

443

- `object_exists`: Object exists in bucket

444

- `object_not_exists`: Object no longer exists in bucket

445

446

### RDS Instance Waiters

447

448

- `db_instance_available`: Database instance is available

449

- `db_instance_deleted`: Database instance has been deleted

450

- `db_snapshot_available`: Database snapshot is available

451

- `db_snapshot_deleted`: Database snapshot has been deleted

452

453

### ELB Waiters

454

455

- `any_instance_in_service`: At least one instance is in service

456

- `instance_deregistered`: Instance is no longer registered

457

- `instance_in_service`: Specific instance is in service

458

459

## Waiter Configuration

460

461

### Timing Configuration

462

463

Control polling behavior through waiter configuration parameters.

464

465

```python

466

waiter_config = {

467

'Delay': 5, # Seconds to wait between attempts

468

'MaxAttempts': 40 # Maximum number of polling attempts

469

}

470

471

waiter.wait(ResourceId='resource-123', WaiterConfig=waiter_config)

472

```

473

474

### Acceptor Types

475

476

Waiters use different matcher types to evaluate responses:

477

478

- **path**: JMESPath expression matches expected value exactly

479

- **pathAll**: All elements in JMESPath result match expected value

480

- **pathAny**: At least one element in JMESPath result matches expected value

481

- **status**: HTTP status code matches expected value

482

- **error**: AWS error code matches expected value (or checks error presence/absence)

483

484

### Custom Waiter Creation

485

486

Create custom waiters using the waiter model system.

487

488

```python

489

from botocore.waiter import WaiterModel, create_waiter_with_client

490

491

# Define custom waiter configuration

492

waiter_config = {

493

'version': 2,

494

'waiters': {

495

'CustomResourceReady': {

496

'delay': 10,

497

'maxAttempts': 30,

498

'operation': 'DescribeCustomResource',

499

'acceptors': [

500

{

501

'matcher': 'path',

502

'expected': 'READY',

503

'argument': 'ResourceStatus',

504

'state': 'success'

505

},

506

{

507

'matcher': 'path',

508

'expected': 'FAILED',

509

'argument': 'ResourceStatus',

510

'state': 'failure'

511

}

512

]

513

}

514

}

515

}

516

517

# Create waiter model and waiter instance

518

waiter_model = WaiterModel(waiter_config)

519

custom_waiter = create_waiter_with_client('CustomResourceReady', waiter_model, client)

520

521

# Use custom waiter

522

custom_waiter.wait(ResourceId='resource-123')

523

```

524

525

## Best Practices

526

527

### Timeout Management

528

529

Always configure appropriate timeouts for your use case.

530

531

```python

532

# For long-running operations, increase max attempts

533

waiter.wait(

534

ResourceId='large-resource',

535

WaiterConfig={

536

'Delay': 30, # Check every 30 seconds

537

'MaxAttempts': 120 # Wait up to 1 hour (30s × 120 = 3600s)

538

}

539

)

540

```

541

542

### Error Recovery

543

544

Implement proper error handling and retry logic.

545

546

```python

547

import time

548

from botocore.exceptions import WaiterError

549

550

def wait_with_retry(waiter, max_retries=3, **kwargs):

551

"""Wait with exponential backoff retry on failures."""

552

for attempt in range(max_retries + 1):

553

try:

554

waiter.wait(**kwargs)

555

return True

556

except WaiterError as e:

557

if attempt == max_retries:

558

raise

559

if 'Max attempts exceeded' in e.reason:

560

# Exponential backoff before retry

561

delay = 2 ** attempt * 60 # 1min, 2min, 4min

562

time.sleep(delay)

563

else:

564

# Don't retry on terminal failures

565

raise

566

return False

567

```

568

569

### Resource Cleanup

570

571

Use waiters to ensure proper resource cleanup.

572

573

```python

574

def cleanup_resources(ec2_client, instance_ids):

575

"""Safely terminate instances and wait for cleanup."""

576

# Terminate instances

577

ec2_client.terminate_instances(InstanceIds=instance_ids)

578

579

# Wait for termination to complete

580

waiter = ec2_client.get_waiter('instance_terminated')

581

waiter.wait(InstanceIds=instance_ids)

582

583

print(f"All instances {instance_ids} have been terminated")

584

```

585

586

### Monitoring Progress

587

588

Combine waiters with logging to monitor long-running operations.

589

590

```python

591

import logging

592

593

logger = logging.getLogger(__name__)

594

595

def wait_with_progress(waiter, operation_name, **kwargs):

596

"""Wait with progress logging."""

597

config = kwargs.get('WaiterConfig', {})

598

max_attempts = config.get('MaxAttempts', waiter.config.max_attempts)

599

delay = config.get('Delay', waiter.config.delay)

600

601

logger.info(f"Starting {operation_name} - will check every {delay}s for up to {max_attempts} attempts")

602

603

try:

604

waiter.wait(**kwargs)

605

logger.info(f"{operation_name} completed successfully")

606

except WaiterError as e:

607

logger.error(f"{operation_name} failed: {e.reason}")

608

raise

609

```