or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-support.mdcallbacks-hooks.mdcore-decorator.mdindex.mdretry-strategies.mdstop-conditions.mdutilities.mdwait-strategies.md

utilities.mddocs/

0

# Utilities & Helpers

1

2

Tenacity provides various utility functions, helper classes, and supporting functionality that enable advanced retry scenarios, time conversion, sleep strategies, and integration with different environments.

3

4

## Exception Classes

5

6

### TryAgain

7

8

```python { .api }

9

from tenacity import TryAgain

10

11

class TryAgain(Exception):

12

"""

13

Exception to force immediate retry regardless of retry condition.

14

15

Raise this exception to bypass all retry strategy evaluation

16

and force another attempt (subject to stop conditions).

17

Useful for explicit retry control within application logic.

18

"""

19

pass

20

```

21

22

#### Usage Examples

23

24

```python { .api }

25

@retry(

26

stop=stop_after_attempt(5),

27

retry=retry_if_exception_type(ValueError) # Only retry ValueErrors normally

28

)

29

def conditional_retry_operation():

30

if some_temporary_condition():

31

# Force retry even though condition might not normally trigger it

32

raise TryAgain

33

34

# Normal operation logic

35

if error_condition():

36

raise ConnectionError("Network failed") # This would NOT normally retry

37

38

return "success"

39

```

40

41

### RetryError

42

43

```python { .api }

44

from tenacity import RetryError

45

46

class RetryError(Exception):

47

"""

48

Exception raised when all retry attempts are exhausted.

49

50

Contains information about the final failed attempt and provides

51

methods to access the original exception that caused the failure.

52

"""

53

54

def __init__(self, last_attempt: Future):

55

"""

56

Initialize with the final failed attempt.

57

58

Parameters:

59

- last_attempt: Future object representing the final attempt

60

"""

61

self.last_attempt = last_attempt

62

63

def reraise(self) -> None:

64

"""

65

Reraise the original exception from the last attempt.

66

67

Raises the actual exception that caused the final failure,

68

preserving the original traceback information.

69

"""

70

71

@property

72

def last_attempt(self) -> Future:

73

"""Access the final failed attempt Future object."""

74

```

75

76

#### RetryError Usage

77

78

```python { .api }

79

from tenacity import RetryError

80

81

def handle_retry_failure():

82

try:

83

result = failing_operation()

84

except RetryError as retry_err:

85

# Access the final attempt

86

final_attempt = retry_err.last_attempt

87

print(f"Failed after attempt {final_attempt.attempt_number}")

88

89

# Get the original exception

90

try:

91

original_result = final_attempt.result() # This will raise the original exception

92

except Exception as original_exc:

93

print(f"Original failure: {original_exc}")

94

95

# Or reraise the original exception directly

96

# retry_err.reraise() # Preserves original traceback

97

98

@retry(

99

stop=stop_after_attempt(3),

100

reraise=False # RetryError will be raised instead of original exception

101

)

102

def failing_operation():

103

raise ValueError("Something went wrong")

104

```

105

106

## Sleep Functions and Strategies

107

108

### Default Sleep Function

109

110

```python { .api }

111

from tenacity.nap import sleep

112

113

def sleep(seconds: float) -> None:

114

"""

115

Default sleep function using time.sleep().

116

117

Parameters:

118

- seconds: Number of seconds to sleep

119

120

Standard blocking sleep implementation used by default

121

in synchronous retry operations.

122

"""

123

```

124

125

### Event-Based Sleep

126

127

```python { .api }

128

from tenacity.nap import sleep_using_event

129

import threading

130

131

class sleep_using_event:

132

"""

133

Sleep strategy that waits on a threading event with timeout.

134

135

Allows external interruption of sleep periods through event signaling.

136

Useful for graceful shutdown scenarios or coordinated stopping.

137

"""

138

139

def __init__(self, event: threading.Event):

140

"""

141

Initialize with threading event for sleep control.

142

143

Parameters:

144

- event: threading.Event that can interrupt sleep when set

145

"""

146

147

def __call__(self, timeout: Optional[float]) -> None:

148

"""

149

Sleep for specified timeout or until event is set.

150

151

Parameters:

152

- timeout: Maximum time to sleep in seconds (None = indefinite)

153

154

Returns immediately if event is already set.

155

"""

156

```

157

158

#### Event Sleep Usage

159

160

```python { .api }

161

import threading

162

163

# Create event for coordinated sleep control

164

shutdown_event = threading.Event()

165

event_sleep = sleep_using_event(shutdown_event)

166

167

@retry(

168

sleep=event_sleep,

169

stop=stop_after_attempt(10),

170

wait=wait_exponential(multiplier=1, max=60)

171

)

172

def interruptible_operation():

173

# This operation's sleep periods can be interrupted

174

pass

175

176

# In another thread or signal handler

177

def shutdown_handler():

178

shutdown_event.set() # Interrupts any ongoing sleep periods

179

```

180

181

## Time Utilities

182

183

### Time Unit Conversion

184

185

```python { .api }

186

from tenacity._utils import time_unit_type, to_seconds

187

from datetime import timedelta

188

189

# Type alias for time specifications

190

time_unit_type = Union[int, float, timedelta]

191

192

def to_seconds(time_unit: time_unit_type) -> float:

193

"""

194

Convert various time units to seconds.

195

196

Parameters:

197

- time_unit: Time specification as int/float (seconds) or timedelta

198

199

Returns:

200

Time converted to seconds as float

201

"""

202

```

203

204

#### Time Conversion Examples

205

206

```python { .api }

207

from datetime import timedelta

208

209

# All equivalent - 30 seconds

210

seconds_int = to_seconds(30) # 30.0

211

seconds_float = to_seconds(30.5) # 30.5

212

seconds_timedelta = to_seconds(timedelta(seconds=30)) # 30.0

213

214

# Complex time specifications

215

minutes = to_seconds(timedelta(minutes=5)) # 300.0

216

mixed = to_seconds(timedelta(minutes=2, seconds=30)) # 150.0

217

hours = to_seconds(timedelta(hours=1)) # 3600.0

218

219

# Usage in retry configurations

220

@retry(

221

stop=stop_after_delay(to_seconds(timedelta(minutes=10))),

222

wait=wait_fixed(to_seconds(timedelta(seconds=30)))

223

)

224

def time_converted_operation():

225

pass

226

```

227

228

### Maximum Wait Constant

229

230

```python { .api }

231

from tenacity._utils import MAX_WAIT

232

import sys

233

234

# Maximum wait time constant

235

MAX_WAIT: float = sys.maxsize / 2

236

```

237

238

## Utility Functions

239

240

### Callback Name Resolution

241

242

```python { .api }

243

from tenacity._utils import get_callback_name

244

245

def get_callback_name(cb: Callable[..., Any]) -> str:

246

"""

247

Get fully qualified name of callback function.

248

249

Parameters:

250

- cb: Callback function to get name for

251

252

Returns:

253

Fully qualified function name as string

254

255

Useful for logging and debugging callback configurations.

256

"""

257

```

258

259

#### Callback Name Usage

260

261

```python { .api }

262

def my_custom_callback(retry_state):

263

pass

264

265

callback_name = get_callback_name(my_custom_callback)

266

print(callback_name) # "my_module.my_custom_callback"

267

268

# Usage in logging

269

def log_callback_config(retrying_instance):

270

before_name = get_callback_name(retrying_instance.before)

271

after_name = get_callback_name(retrying_instance.after)

272

print(f"Before callback: {before_name}")

273

print(f"After callback: {after_name}")

274

```

275

276

### Ordinal Number Conversion

277

278

```python { .api }

279

from tenacity._utils import to_ordinal

280

281

def to_ordinal(pos_num: int) -> str:

282

"""

283

Convert positive number to ordinal string representation.

284

285

Parameters:

286

- pos_num: Positive integer to convert

287

288

Returns:

289

Ordinal string (1st, 2nd, 3rd, 4th, etc.)

290

291

Useful for human-friendly logging and error messages.

292

"""

293

```

294

295

#### Ordinal Usage Examples

296

297

```python { .api }

298

# Convert numbers to ordinals

299

print(to_ordinal(1)) # "1st"

300

print(to_ordinal(2)) # "2nd"

301

print(to_ordinal(3)) # "3rd"

302

print(to_ordinal(4)) # "4th"

303

print(to_ordinal(21)) # "21st"

304

print(to_ordinal(22)) # "22nd"

305

306

# Usage in retry messages

307

def ordinal_logging_callback(retry_state):

308

attempt_ordinal = to_ordinal(retry_state.attempt_number)

309

print(f"Starting {attempt_ordinal} attempt at {retry_state.fn.__name__}")

310

311

@retry(before=ordinal_logging_callback)

312

def operation_with_ordinal_logging():

313

pass

314

```

315

316

## Async Utilities

317

318

### Coroutine Detection

319

320

```python { .api }

321

from tenacity._utils import is_coroutine_callable

322

323

def is_coroutine_callable(call: Callable[..., Any]) -> bool:

324

"""

325

Check if callable is a coroutine function.

326

327

Parameters:

328

- call: Callable to check

329

330

Returns:

331

True if callable is async/coroutine function, False otherwise

332

333

Used internally to detect async functions for automatic

334

AsyncRetrying application.

335

"""

336

```

337

338

### Async Function Wrapping

339

340

```python { .api }

341

from tenacity._utils import wrap_to_async_func

342

343

def wrap_to_async_func(call: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:

344

"""

345

Wrap synchronous function to async, or return as-is if already async.

346

347

Parameters:

348

- call: Function to wrap (sync or async)

349

350

Returns:

351

Async function that can be awaited

352

353

Enables using sync callbacks in async retry contexts.

354

"""

355

```

356

357

#### Async Utility Usage

358

359

```python { .api }

360

# Check if function is async

361

def sync_function():

362

return "sync result"

363

364

async def async_function():

365

return "async result"

366

367

print(is_coroutine_callable(sync_function)) # False

368

print(is_coroutine_callable(async_function)) # True

369

370

# Wrap sync function for async use

371

wrapped_sync = wrap_to_async_func(sync_function)

372

wrapped_async = wrap_to_async_func(async_function) # Returns as-is

373

374

# Both can now be awaited

375

async def demo():

376

result1 = await wrapped_sync() # Works

377

result2 = await wrapped_async() # Works

378

```

379

380

## Constants and Sentinels

381

382

### NO_RESULT Sentinel

383

384

```python { .api }

385

from tenacity import NO_RESULT

386

387

# Sentinel object for unset results

388

NO_RESULT: object

389

```

390

391

#### NO_RESULT Usage

392

393

```python { .api }

394

def check_result_status(retry_state):

395

if retry_state.outcome is NO_RESULT:

396

print("No result set yet")

397

elif retry_state.outcome.failed:

398

print("Attempt failed")

399

else:

400

print("Attempt succeeded")

401

```

402

403

## Advanced Utility Patterns

404

405

### Custom Sleep Implementations

406

407

```python { .api }

408

import asyncio

409

import time

410

411

class ProgressiveSleep:

412

"""Custom sleep with progress indication."""

413

414

def __init__(self, progress_callback=None):

415

self.progress_callback = progress_callback

416

417

def __call__(self, seconds):

418

if self.progress_callback:

419

# Show progress during long sleeps

420

for i in range(int(seconds)):

421

time.sleep(1)

422

self.progress_callback(i + 1, int(seconds))

423

# Handle fractional remainder

424

remainder = seconds - int(seconds)

425

if remainder > 0:

426

time.sleep(remainder)

427

else:

428

time.sleep(seconds)

429

430

# Usage with progress indication

431

def progress_indicator(current, total):

432

print(f"Sleeping... {current}/{total} seconds")

433

434

progressive_sleep = ProgressiveSleep(progress_indicator)

435

436

@retry(

437

sleep=progressive_sleep,

438

wait=wait_exponential(multiplier=1, min=5, max=30)

439

)

440

def operation_with_progress():

441

pass

442

```

443

444

### Retry State Analysis

445

446

```python { .api }

447

def analyze_retry_state(retry_state: RetryCallState) -> dict:

448

"""Comprehensive retry state analysis."""

449

return {

450

'function_name': retry_state.fn.__name__,

451

'attempt_number': retry_state.attempt_number,

452

'elapsed_seconds': retry_state.seconds_since_start,

453

'total_idle_time': retry_state.idle_for,

454

'active_time': retry_state.seconds_since_start - retry_state.idle_for,

455

'last_outcome': 'failed' if retry_state.outcome and retry_state.outcome.failed else 'success',

456

'upcoming_sleep': retry_state.upcoming_sleep if retry_state.upcoming_sleep else 0,

457

'efficiency': (retry_state.seconds_since_start - retry_state.idle_for) / retry_state.seconds_since_start if retry_state.seconds_since_start > 0 else 1.0

458

}

459

460

def detailed_analysis_callback(retry_state):

461

analysis = analyze_retry_state(retry_state)

462

print(f"Retry Analysis: {analysis}")

463

464

@retry(after=detailed_analysis_callback)

465

def analyzed_operation():

466

pass

467

```

468

469

### Time Zone Aware Utilities

470

471

```python { .api }

472

from datetime import datetime, timezone

473

import time

474

475

class TimeZoneAwareSleep:

476

"""Sleep implementation that considers time zones."""

477

478

def __init__(self, timezone_offset=0):

479

self.timezone_offset = timezone_offset

480

481

def __call__(self, seconds):

482

# Could adjust sleep based on time zone considerations

483

current_hour = datetime.now(timezone.utc).hour + self.timezone_offset

484

485

# Example: longer sleeps during business hours

486

if 9 <= current_hour <= 17:

487

adjusted_seconds = seconds * 1.5 # 50% longer during business hours

488

else:

489

adjusted_seconds = seconds

490

491

time.sleep(adjusted_seconds)

492

493

# Usage for timezone-aware retry behavior

494

tz_sleep = TimeZoneAwareSleep(timezone_offset=-8) # PST

495

496

@retry(

497

sleep=tz_sleep,

498

wait=wait_exponential(multiplier=1, max=60)

499

)

500

def timezone_aware_operation():

501

pass

502

```

503

504

### Performance Monitoring Utilities

505

506

```python { .api }

507

import time

508

import threading

509

from collections import defaultdict

510

511

class RetryPerformanceMonitor:

512

"""Monitor retry performance across the application."""

513

514

def __init__(self):

515

self.stats = defaultdict(list)

516

self.lock = threading.Lock()

517

518

def record_attempt(self, retry_state):

519

with self.lock:

520

function_name = retry_state.fn.__name__

521

self.stats[function_name].append({

522

'attempt': retry_state.attempt_number,

523

'timestamp': time.time(),

524

'elapsed': retry_state.seconds_since_start,

525

'success': not (retry_state.outcome and retry_state.outcome.failed)

526

})

527

528

def get_summary(self, function_name=None):

529

with self.lock:

530

if function_name:

531

return self._summarize_function(function_name, self.stats[function_name])

532

else:

533

return {fn: self._summarize_function(fn, attempts)

534

for fn, attempts in self.stats.items()}

535

536

def _summarize_function(self, function_name, attempts):

537

if not attempts:

538

return {'total_attempts': 0}

539

540

total_attempts = len(attempts)

541

successful_operations = len([a for a in attempts if a['success']])

542

avg_duration = sum(a['elapsed'] for a in attempts) / total_attempts

543

544

return {

545

'function_name': function_name,

546

'total_attempts': total_attempts,

547

'successful_operations': successful_operations,

548

'success_rate': successful_operations / total_attempts,

549

'avg_duration': avg_duration

550

}

551

552

# Global performance monitor

553

perf_monitor = RetryPerformanceMonitor()

554

555

@retry(after=perf_monitor.record_attempt)

556

def monitored_operation():

557

pass

558

559

# Get performance summary

560

summary = perf_monitor.get_summary()

561

print(summary)

562

```

563

564

### Resource Management Utilities

565

566

```python { .api }

567

import weakref

568

from contextlib import contextmanager

569

570

class RetryResourceManager:

571

"""Manage resources across retry attempts."""

572

573

def __init__(self):

574

self.active_retries = weakref.WeakSet()

575

576

def register_retry(self, retry_state):

577

self.active_retries.add(retry_state)

578

579

def get_active_count(self):

580

return len(self.active_retries)

581

582

@contextmanager

583

def resource_context(self, retry_state):

584

self.register_retry(retry_state)

585

try:

586

yield

587

finally:

588

# Cleanup happens automatically via WeakSet

589

pass

590

591

# Global resource manager

592

resource_manager = RetryResourceManager()

593

594

def resource_aware_callback(retry_state):

595

active_count = resource_manager.get_active_count()

596

if active_count > 10:

597

print(f"Warning: {active_count} active retry operations")

598

599

@retry(

600

before=resource_aware_callback,

601

before_sleep=lambda rs: resource_manager.register_retry(rs)

602

)

603

def resource_managed_operation():

604

pass

605

```

606

607

This comprehensive collection of utilities and helpers provides the building blocks for advanced retry scenarios, performance monitoring, resource management, and integration with various system components.