Skills · Coding

Python Resilience Patterns

Unverified29/40

Python resilience patterns including automatic retries, exponential backoff, timeouts, and fault-tolerant decorators. Use when adding retry logic, implementing timeouts, building fault-tolerant services, or handling transient failures.

Originally by wshobson · MIT

Claude CodePartialHas SKILL.md but declares no allowed-tools — Claude Code will ask for permission each time
CursorPartialPlain prose you can paste in — but no Cursor rules file
CodexPartialPlain prose you can paste in — but no AGENTS.md
Gemini CLIPartialPlain prose you can paste in
CopilotPartialPlain prose you can paste in — but no Copilot instructions file
npx agentalley add python-resilience

This command does not work yet — the CLI is still being built. Until then, use Raw in the reader below to take the file.

Who is stuck, and on what

Python resilience patterns including automatic retries, exponential backoff, timeouts, and fault-tolerant decorators. Use when adding retry logic, implementing timeouts, building fault-tolerant services, or handling transient failures.

The whole source

No sign-in, no blur, nothing truncated
python-resilience/SKILL.md196 lines5.9 KBRawView on GitHub
Frontmatter — 2 properties
namepython-resilience
descriptionPython resilience patterns including automatic retries, exponential backoff, timeouts, and fault-tolerant decorators. Use when adding retry logic, implementing timeouts, building fault-tolerant services, or handling transient failures.
1---
2name: python-resilience
3description: Python resilience patterns including automatic retries, exponential backoff, timeouts, and fault-tolerant decorators. Use when adding retry logic, implementing timeouts, building fault-tolerant services, or handling transient failures.
4---A5No allowed-tools declared — no way to tell what this skill may touch
5 
6# Python Resilience Patterns
7 
8Build fault-tolerant Python applications that gracefully handle transient failures, network issues, and service outages. Resilience patterns keep systems running when dependencies are unreliable.
9 
10## When to Use This Skill
11 
12- Adding retry logic to external service calls
13- Implementing timeouts for network operations
14- Building fault-tolerant microservices
15- Handling rate limiting and backpressure
16- Creating infrastructure decorators
17- Designing circuit breakers
18 
19## Core Concepts
20 
21### 1. Transient vs Permanent Failures
22 
23Retry transient errors (network timeouts, temporary service issues). Don't retry permanent errors (invalid credentials, bad requests).
24 
25### 2. Exponential Backoff
26 
27Increase wait time between retries to avoid overwhelming recovering services.
28 
29### 3. Jitter
30 
31Add randomness to backoff to prevent thundering herd when many clients retry simultaneously.
32 
33### 4. Bounded Retries
34 
35Cap both attempt count and total duration to prevent infinite retry loops.
36 
37## Quick Start
38 
39```python
40from tenacity import retry, stop_after_attempt, wait_exponential_jitter
41 
42@retry(
43 stop=stop_after_attempt(3),
44 wait=wait_exponential_jitter(initial=1, max=10),
45)
46def call_external_service(request: dict) -> dict:
47 return httpx.post("https://api.example.com", json=request).json()
48```
49 
50## Fundamental Patterns
51 
52### Pattern 1: Basic Retry with Tenacity
53 
54Use the `tenacity` library for production-grade retry logic. For simpler cases, consider built-in retry functionality or a lightweight custom implementation.
55 
56```python
57from tenacity import (
58 retry,
59 stop_after_attempt,
60 stop_after_delay,
61 wait_exponential_jitter,
62 retry_if_exception_type,
63)
64 
65TRANSIENT_ERRORS = (ConnectionError, TimeoutError, OSError)
66 
67@retry(
68 retry=retry_if_exception_type(TRANSIENT_ERRORS),
69 stop=stop_after_attempt(5) | stop_after_delay(60),
70 wait=wait_exponential_jitter(initial=1, max=30),
71)
72def fetch_data(url: str) -> dict:
73 """Fetch data with automatic retry on transient failures."""A4This skill pulls in web or user content but never says to treat that content as data. A signal, not proof.
74 response = httpx.get(url, timeout=30)
75 response.raise_for_status()
76 return response.json()
77```
78 
79### Pattern 2: Retry Only Appropriate Errors
80 
81Whitelist specific transient exceptions. Never retry:
82 
83- `ValueError`, `TypeError` - These are bugs, not transient issues
84- `AuthenticationError` - Invalid credentials won't become valid
85- HTTP 4xx errors (except 429) - Client errors are permanent
86 
87```python
88from tenacity import retry, retry_if_exception_type
89import httpx
90 
91# Define what's retryable
92RETRYABLE_EXCEPTIONS = (
93 ConnectionError,
94 TimeoutError,
95 httpx.ConnectTimeout,
96 httpx.ReadTimeout,
97)
98 
99@retry(
100 retry=retry_if_exception_type(RETRYABLE_EXCEPTIONS),
101 stop=stop_after_attempt(3),
102 wait=wait_exponential_jitter(initial=1, max=10),
103)
104def resilient_api_call(endpoint: str) -> dict:
105 """Make API call with retry on network issues."""
106 return httpx.get(endpoint, timeout=10).json()
107```
108 
109### Pattern 3: HTTP Status Code Retries
110 
111Retry specific HTTP status codes that indicate transient issues.
112 
113```python
114from tenacity import retry, retry_if_result, stop_after_attempt
115import httpx
116 
117RETRY_STATUS_CODES = {429, 502, 503, 504}
118 
119def should_retry_response(response: httpx.Response) -> bool:
120 """Check if response indicates a retryable error."""
121 return response.status_code in RETRY_STATUS_CODES
122 
123@retry(
124 retry=retry_if_result(should_retry_response),
125 stop=stop_after_attempt(3),
126 wait=wait_exponential_jitter(initial=1, max=10),
127)
128def http_request(method: str, url: str, **kwargs) -> httpx.Response:
129 """Make HTTP request with retry on transient status codes."""
130 return httpx.request(method, url, timeout=30, **kwargs)
131```
132 
133### Pattern 4: Combined Exception and Status Retry
134 
135Handle both network exceptions and HTTP status codes.
136 
137```python
138from tenacity import (
139 retry,
140 retry_if_exception_type,
141 retry_if_result,
142 stop_after_attempt,
143 wait_exponential_jitter,
144 before_sleep_log,
145)
146import logging
147import httpx
148 
149logger = logging.getLogger(__name__)
150 
151TRANSIENT_EXCEPTIONS = (
152 ConnectionError,
153 TimeoutError,
154 httpx.ConnectError,
155 httpx.ReadTimeout,
156)
157RETRY_STATUS_CODES = {429, 500, 502, 503, 504}
158 
159def is_retryable_response(response: httpx.Response) -> bool:
160 return response.status_code in RETRY_STATUS_CODES
161 
162@retry(
163 retry=(
164 retry_if_exception_type(TRANSIENT_EXCEPTIONS) |
165 retry_if_result(is_retryable_response)
166 ),
167 stop=stop_after_attempt(5),
168 wait=wait_exponential_jitter(initial=1, max=30),
169 before_sleep=before_sleep_log(logger, logging.WARNING),
170)
171def robust_http_call(
172 method: str,
173 url: str,
174 **kwargs,
175) -> httpx.Response:
176 """HTTP call with comprehensive retry handling."""
177 return httpx.request(method, url, timeout=30, **kwargs)
178```
179 
180## Detailed worked examples and patterns
181 
182Detailed sections (starting with `## Advanced Patterns`) live in `references/details.md`. Read that file when the navigation summary above is insufficient.
183 
184## Best Practices Summary
185 
1861. **Retry only transient errors** - Don't retry bugs or authentication failures
1872. **Use exponential backoff** - Give services time to recover
1883. **Add jitter** - Prevent thundering herd from synchronized retries
1894. **Cap total duration** - `stop_after_attempt(5) | stop_after_delay(60)`
1905. **Log every retry** - Silent retries hide systemic problems
1916. **Use decorators** - Keep retry logic separate from business logic
1927. **Inject dependencies** - Make infrastructure testable
1938. **Set timeouts everywhere** - Every network call needs a timeout
1949. **Fail gracefully** - Return cached/default values for non-critical paths
19510. **Monitor retry rates** - High retry rates indicate underlying issues
196 

Reviews

Installed this one?Write the first review and take the Trailblazer badge.

Reviews only open after a real install, so this is empty — and we leave it empty rather than invent one.

Alternatives

Also in Coding