@sylix/coworker 2.0.11 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/slash/config.d.ts.map +1 -1
- package/dist/commands/slash/config.js +22 -4
- package/dist/commands/slash/config.js.map +1 -1
- package/dist/core/CoWorkerAgent.d.ts.map +1 -1
- package/dist/core/CoWorkerAgent.js +6 -3
- package/dist/core/CoWorkerAgent.js.map +1 -1
- package/dist/skills/defaults/accessibility/screen-reader-testing.md +545 -0
- package/dist/skills/defaults/accessibility/wcag-audit-patterns.md +555 -0
- package/dist/skills/defaults/ai-ml/rag.md +276 -0
- package/dist/skills/defaults/backend-development/api-design-principles.md +528 -0
- package/dist/skills/defaults/backend-development/api-design.md +285 -0
- package/dist/skills/defaults/backend-development/architecture-patterns.md +494 -0
- package/dist/skills/defaults/backend-development/async-python.md +237 -0
- package/dist/skills/defaults/backend-development/auth-implementation-patterns.md +638 -0
- package/dist/skills/defaults/backend-development/bazel-build-optimization.md +387 -0
- package/dist/skills/defaults/backend-development/billing-automation/SKILL.md +566 -0
- package/dist/skills/defaults/backend-development/code-review-excellence.md +538 -0
- package/dist/skills/defaults/backend-development/cqrs-implementation.md +554 -0
- package/dist/skills/defaults/backend-development/database-design.md +305 -0
- package/dist/skills/defaults/backend-development/debugging-strategies.md +536 -0
- package/dist/skills/defaults/backend-development/e2e-testing-patterns.md +544 -0
- package/dist/skills/defaults/backend-development/error-handling-patterns.md +641 -0
- package/dist/skills/defaults/backend-development/fastapi-templates.md +559 -0
- package/dist/skills/defaults/backend-development/fastapi.md +309 -0
- package/dist/skills/defaults/backend-development/git-advanced-workflows.md +405 -0
- package/dist/skills/defaults/backend-development/microservices-patterns.md +595 -0
- package/dist/skills/defaults/backend-development/microservices.md +284 -0
- package/dist/skills/defaults/backend-development/monorepo-management.md +623 -0
- package/dist/skills/defaults/backend-development/nodejs-backend-patterns.md +1048 -0
- package/dist/skills/defaults/backend-development/nx-workspace-patterns.md +457 -0
- package/dist/skills/defaults/backend-development/paypal-integration/SKILL.md +478 -0
- package/dist/skills/defaults/backend-development/pci-compliance/SKILL.md +480 -0
- package/dist/skills/defaults/backend-development/python-anti-patterns.md +349 -0
- package/dist/skills/defaults/backend-development/python-background-jobs.md +364 -0
- package/dist/skills/defaults/backend-development/python-code-style.md +360 -0
- package/dist/skills/defaults/backend-development/python-configuration.md +368 -0
- package/dist/skills/defaults/backend-development/python-design-patterns.md +296 -0
- package/dist/skills/defaults/backend-development/python-error-handling.md +323 -0
- package/dist/skills/defaults/backend-development/python-packaging.md +887 -0
- package/dist/skills/defaults/backend-development/python-performance-optimization.md +874 -0
- package/dist/skills/defaults/backend-development/python-project-structure.md +252 -0
- package/dist/skills/defaults/backend-development/python-resilience.md +376 -0
- package/dist/skills/defaults/backend-development/python-resource-management.md +421 -0
- package/dist/skills/defaults/backend-development/python-type-safety.md +428 -0
- package/dist/skills/defaults/backend-development/sql-optimization-patterns.md +509 -0
- package/dist/skills/defaults/backend-development/stripe-integration/SKILL.md +522 -0
- package/dist/skills/defaults/backend-development/turborepo-caching.md +376 -0
- package/dist/skills/defaults/blockchain/defi-protocol-templates.md +430 -0
- package/dist/skills/defaults/blockchain/nft-standards.md +364 -0
- package/dist/skills/defaults/blockchain/solidity-security.md +514 -0
- package/dist/skills/defaults/blockchain/web3-testing.md +360 -0
- package/dist/skills/defaults/business/competitive-landscape/SKILL.md +527 -0
- package/dist/skills/defaults/business/market-sizing-analysis/SKILL.md +451 -0
- package/dist/skills/defaults/business/startup-financial-modeling/SKILL.md +494 -0
- package/dist/skills/defaults/business/startup-metrics-framework/SKILL.md +564 -0
- package/dist/skills/defaults/business/team-composition-analysis.md +437 -0
- package/dist/skills/defaults/compliance/employment-contract-templates/SKILL.md +527 -0
- package/dist/skills/defaults/compliance/gdpr-data-handling/SKILL.md +630 -0
- package/dist/skills/defaults/data-engineering/airflow-dag-patterns.md +436 -0
- package/dist/skills/defaults/data-engineering/airflow.md +519 -0
- package/dist/skills/defaults/data-engineering/data-quality.md +583 -0
- package/dist/skills/defaults/data-engineering/dbt-transformation-patterns.md +482 -0
- package/dist/skills/defaults/data-engineering/dbt.md +556 -0
- package/dist/skills/defaults/data-engineering/ml-pipeline-workflow/SKILL.md +247 -0
- package/dist/skills/defaults/data-engineering/spark-optimization.md +348 -0
- package/dist/skills/defaults/data-engineering/spark.md +411 -0
- package/dist/skills/defaults/database/postgresql.md +202 -0
- package/dist/skills/defaults/debugging/systematic-debugging.md +249 -0
- package/dist/skills/defaults/devops/architecture-decision-records.md +448 -0
- package/dist/skills/defaults/devops/changelog-automation.md +580 -0
- package/dist/skills/defaults/devops/cicd.md +314 -0
- package/dist/skills/defaults/devops/cloud.md +263 -0
- package/dist/skills/defaults/devops/code-review-excellence.md +299 -0
- package/dist/skills/defaults/devops/cost-optimization.md +295 -0
- package/dist/skills/defaults/devops/deployment-pipeline-design.md +356 -0
- package/dist/skills/defaults/devops/docker.md +281 -0
- package/dist/skills/defaults/devops/git-workflows.md +205 -0
- package/dist/skills/defaults/devops/github-actions.md +311 -0
- package/dist/skills/defaults/devops/gitlab-ci-patterns.md +266 -0
- package/dist/skills/defaults/devops/hybrid-cloud-networking.md +241 -0
- package/dist/skills/defaults/devops/istio-traffic-management.md +327 -0
- package/dist/skills/defaults/devops/kubernetes.md +339 -0
- package/dist/skills/defaults/devops/linkerd-patterns.md +311 -0
- package/dist/skills/defaults/devops/multi-cloud-architecture.md +181 -0
- package/dist/skills/defaults/devops/observability.md +243 -0
- package/dist/skills/defaults/devops/openapi-spec-generation.md +1024 -0
- package/dist/skills/defaults/devops/postmortem-writing.md +396 -0
- package/dist/skills/defaults/devops/prometheus-configuration.md +265 -0
- package/dist/skills/defaults/devops/secrets-management.md +341 -0
- package/dist/skills/defaults/devops/service-mesh-observability.md +385 -0
- package/dist/skills/defaults/devops/terraform-module-library.md +244 -0
- package/dist/skills/defaults/finance/backtesting-frameworks/SKILL.md +663 -0
- package/dist/skills/defaults/finance/risk-metrics-calculation/SKILL.md +557 -0
- package/dist/skills/defaults/frontend/accessibility-compliance.md +420 -0
- package/dist/skills/defaults/frontend/design-system-patterns.md +337 -0
- package/dist/skills/defaults/frontend/interaction-design.md +327 -0
- package/dist/skills/defaults/frontend/javascript.md +311 -0
- package/dist/skills/defaults/frontend/modern-javascript-patterns.md +927 -0
- package/dist/skills/defaults/frontend/react-native-design.md +440 -0
- package/dist/skills/defaults/frontend/react.md +345 -0
- package/dist/skills/defaults/frontend/responsive-design.md +472 -0
- package/dist/skills/defaults/frontend/tailwind-design-system.md +337 -0
- package/dist/skills/defaults/frontend/typescript-advanced-types.md +724 -0
- package/dist/skills/defaults/frontend/typescript.md +334 -0
- package/dist/skills/defaults/frontend/visual-design-foundations.md +326 -0
- package/dist/skills/defaults/frontend/web-component-design.md +279 -0
- package/dist/skills/defaults/game-development/godot-gdscript-patterns.md +188 -0
- package/dist/skills/defaults/game-development/unity-ecs-patterns.md +594 -0
- package/dist/skills/defaults/kubernetes/gitops-workflow.md +285 -0
- package/dist/skills/defaults/kubernetes/gitops.md +280 -0
- package/dist/skills/defaults/kubernetes/helm-chart-scaffolding.md +553 -0
- package/dist/skills/defaults/kubernetes/helm.md +343 -0
- package/dist/skills/defaults/kubernetes/k8s-manifest-generator.md +501 -0
- package/dist/skills/defaults/kubernetes/k8s-security-policies.md +342 -0
- package/dist/skills/defaults/kubernetes/manifests.md +330 -0
- package/dist/skills/defaults/kubernetes/security.md +337 -0
- package/dist/skills/defaults/llm-application/embedding-strategies.md +608 -0
- package/dist/skills/defaults/llm-application/hybrid-search-implementation.md +570 -0
- package/dist/skills/defaults/llm-application/hybrid-search.md +570 -0
- package/dist/skills/defaults/llm-application/langchain-architecture.md +666 -0
- package/dist/skills/defaults/llm-application/langchain.md +259 -0
- package/dist/skills/defaults/llm-application/llm-evaluation.md +695 -0
- package/dist/skills/defaults/llm-application/prompt-engineering-patterns.md +449 -0
- package/dist/skills/defaults/llm-application/prompt-engineering.md +219 -0
- package/dist/skills/defaults/llm-application/rag-implementation.md +434 -0
- package/dist/skills/defaults/llm-application/similarity-search-patterns.md +560 -0
- package/dist/skills/defaults/llm-application/similarity-search.md +560 -0
- package/dist/skills/defaults/llm-application/vector-index-tuning.md +523 -0
- package/dist/skills/defaults/mobile/mobile-android-design.md +440 -0
- package/dist/skills/defaults/mobile/mobile-ios-design.md +266 -0
- package/dist/skills/defaults/monitoring/distributed-tracing.md +436 -0
- package/dist/skills/defaults/monitoring/grafana-dashboards.md +370 -0
- package/dist/skills/defaults/monitoring/prometheus-configuration.md +379 -0
- package/dist/skills/defaults/monitoring/slo-implementation.md +323 -0
- package/dist/skills/defaults/refactoring/code-refactoring.md +349 -0
- package/dist/skills/defaults/security/anti-reversing-techniques/SKILL.md +559 -0
- package/dist/skills/defaults/security/auditor.md +168 -0
- package/dist/skills/defaults/security/binary-analysis-patterns/SKILL.md +438 -0
- package/dist/skills/defaults/security/memory-forensics/SKILL.md +483 -0
- package/dist/skills/defaults/security/mtls-configuration.md +349 -0
- package/dist/skills/defaults/security/protocol-reverse-engineering/SKILL.md +520 -0
- package/dist/skills/defaults/security/sast-configuration.md +182 -0
- package/dist/skills/defaults/security/security.md +313 -0
- package/dist/skills/defaults/security/stride-analysis.md +273 -0
- package/dist/skills/defaults/security/threat-mitigation-mapping.md +290 -0
- package/dist/skills/defaults/systems/bash-defensive-patterns/SKILL.md +539 -0
- package/dist/skills/defaults/systems/bats-testing-patterns/SKILL.md +631 -0
- package/dist/skills/defaults/systems/go-concurrency-patterns.md +657 -0
- package/dist/skills/defaults/systems/memory-safety-patterns.md +605 -0
- package/dist/skills/defaults/systems/rust-async-patterns.md +519 -0
- package/dist/skills/defaults/systems/shellcheck-configuration/SKILL.md +456 -0
- package/dist/skills/defaults/team-collaboration/multi-reviewer-patterns.md +126 -0
- package/dist/skills/defaults/team-collaboration/parallel-feature-development.md +151 -0
- package/dist/skills/defaults/testing/javascript-testing-patterns.md +1021 -0
- package/dist/skills/defaults/testing/python-testing-patterns.md +351 -0
- package/dist/skills/defaults/testing/testing.md +332 -0
- package/dist/skills/defaults/workflows/context-driven-development.md +384 -0
- package/dist/skills/defaults/workflows/track-management.md +592 -0
- package/dist/skills/defaults/workflows/workflow-patterns.md +622 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +129 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/utils/character.js +4 -4
- package/dist/utils/character.js.map +1 -1
- package/dist/utils/inputbar.d.ts.map +1 -1
- package/dist/utils/inputbar.js +7 -0
- package/dist/utils/inputbar.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-project-structure
|
|
3
|
+
description: Python project organization, module architecture, and public API design. Use when setting up new projects, organizing modules, defining public interfaces with __all__, or planning directory layouts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Project Structure & Module Architecture
|
|
7
|
+
|
|
8
|
+
Design well-organized Python projects with clear module boundaries, explicit public interfaces, and maintainable directory structures. Good organization makes code discoverable and changes predictable.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- Starting a new Python project from scratch
|
|
13
|
+
- Reorganizing an existing codebase for clarity
|
|
14
|
+
- Defining module public APIs with `__all__`
|
|
15
|
+
- Deciding between flat and nested directory structures
|
|
16
|
+
- Determining test file placement strategies
|
|
17
|
+
- Creating reusable library packages
|
|
18
|
+
|
|
19
|
+
## Core Concepts
|
|
20
|
+
|
|
21
|
+
### 1. Module Cohesion
|
|
22
|
+
|
|
23
|
+
Group related code that changes together. A module should have a single, clear purpose.
|
|
24
|
+
|
|
25
|
+
### 2. Explicit Interfaces
|
|
26
|
+
|
|
27
|
+
Define what's public with `__all__`. Everything not listed is an internal implementation detail.
|
|
28
|
+
|
|
29
|
+
### 3. Flat Hierarchies
|
|
30
|
+
|
|
31
|
+
Prefer shallow directory structures. Add depth only for genuine sub-domains.
|
|
32
|
+
|
|
33
|
+
### 4. Consistent Conventions
|
|
34
|
+
|
|
35
|
+
Apply naming and organization patterns uniformly across the project.
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
myproject/
|
|
41
|
+
├── src/
|
|
42
|
+
│ └── myproject/
|
|
43
|
+
│ ├── __init__.py
|
|
44
|
+
│ ├── services/
|
|
45
|
+
│ ├── models/
|
|
46
|
+
│ └── api/
|
|
47
|
+
├── tests/
|
|
48
|
+
├── pyproject.toml
|
|
49
|
+
└── README.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Fundamental Patterns
|
|
53
|
+
|
|
54
|
+
### Pattern 1: One Concept Per File
|
|
55
|
+
|
|
56
|
+
Each file should focus on a single concept or closely related set of functions. Consider splitting when a file:
|
|
57
|
+
|
|
58
|
+
- Handles multiple unrelated responsibilities
|
|
59
|
+
- Grows beyond 300-500 lines (varies by complexity)
|
|
60
|
+
- Contains classes that change for different reasons
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# Good: Focused files
|
|
64
|
+
# user_service.py - User business logic
|
|
65
|
+
# user_repository.py - User data access
|
|
66
|
+
# user_models.py - User data structures
|
|
67
|
+
|
|
68
|
+
# Avoid: Kitchen sink files
|
|
69
|
+
# user.py - Contains service, repository, models, utilities...
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Pattern 2: Explicit Public APIs with `__all__`
|
|
73
|
+
|
|
74
|
+
Define the public interface for every module. Unlisted members are internal implementation details.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# mypackage/services/__init__.py
|
|
78
|
+
from .user_service import UserService
|
|
79
|
+
from .order_service import OrderService
|
|
80
|
+
from .exceptions import ServiceError, ValidationError
|
|
81
|
+
|
|
82
|
+
__all__ = [
|
|
83
|
+
"UserService",
|
|
84
|
+
"OrderService",
|
|
85
|
+
"ServiceError",
|
|
86
|
+
"ValidationError",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
# Internal helpers remain private by omission
|
|
90
|
+
# from .internal_helpers import _validate_input # Not exported
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Pattern 3: Flat Directory Structure
|
|
94
|
+
|
|
95
|
+
Prefer minimal nesting. Deep hierarchies make imports verbose and navigation difficult.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
# Preferred: Flat structure
|
|
99
|
+
project/
|
|
100
|
+
├── api/
|
|
101
|
+
│ ├── routes.py
|
|
102
|
+
│ └── middleware.py
|
|
103
|
+
├── services/
|
|
104
|
+
│ ├── user_service.py
|
|
105
|
+
│ └── order_service.py
|
|
106
|
+
├── models/
|
|
107
|
+
│ ├── user.py
|
|
108
|
+
│ └── order.py
|
|
109
|
+
└── utils/
|
|
110
|
+
└── validation.py
|
|
111
|
+
|
|
112
|
+
# Avoid: Deep nesting
|
|
113
|
+
project/core/internal/services/impl/user/
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Add sub-packages only when there's a genuine sub-domain requiring isolation.
|
|
117
|
+
|
|
118
|
+
### Pattern 4: Test File Organization
|
|
119
|
+
|
|
120
|
+
Choose one approach and apply it consistently throughout the project.
|
|
121
|
+
|
|
122
|
+
**Option A: Colocated Tests**
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
src/
|
|
126
|
+
├── user_service.py
|
|
127
|
+
├── test_user_service.py
|
|
128
|
+
├── order_service.py
|
|
129
|
+
└── test_order_service.py
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Benefits: Tests live next to the code they verify. Easy to see coverage gaps.
|
|
133
|
+
|
|
134
|
+
**Option B: Parallel Test Directory**
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
src/
|
|
138
|
+
├── services/
|
|
139
|
+
│ ├── user_service.py
|
|
140
|
+
│ └── order_service.py
|
|
141
|
+
tests/
|
|
142
|
+
├── services/
|
|
143
|
+
│ ├── test_user_service.py
|
|
144
|
+
│ └── test_order_service.py
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Benefits: Clean separation between production and test code. Standard for larger projects.
|
|
148
|
+
|
|
149
|
+
## Advanced Patterns
|
|
150
|
+
|
|
151
|
+
### Pattern 5: Package Initialization
|
|
152
|
+
|
|
153
|
+
Use `__init__.py` to provide a clean public interface for package consumers.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# mypackage/__init__.py
|
|
157
|
+
"""MyPackage - A library for doing useful things."""
|
|
158
|
+
|
|
159
|
+
from .core import MainClass, HelperClass
|
|
160
|
+
from .exceptions import PackageError, ConfigError
|
|
161
|
+
from .config import Settings
|
|
162
|
+
|
|
163
|
+
__all__ = [
|
|
164
|
+
"MainClass",
|
|
165
|
+
"HelperClass",
|
|
166
|
+
"PackageError",
|
|
167
|
+
"ConfigError",
|
|
168
|
+
"Settings",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
__version__ = "1.0.0"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Consumers can then import directly from the package:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from mypackage import MainClass, Settings
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Pattern 6: Layered Architecture
|
|
181
|
+
|
|
182
|
+
Organize code by architectural layer for clear separation of concerns.
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
myapp/
|
|
186
|
+
├── api/ # HTTP handlers, request/response
|
|
187
|
+
│ ├── routes/
|
|
188
|
+
│ └── middleware/
|
|
189
|
+
├── services/ # Business logic
|
|
190
|
+
├── repositories/ # Data access
|
|
191
|
+
├── models/ # Domain entities
|
|
192
|
+
├── schemas/ # API schemas (Pydantic)
|
|
193
|
+
└── config/ # Configuration
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Each layer should only depend on layers below it, never above.
|
|
197
|
+
|
|
198
|
+
### Pattern 7: Domain-Driven Structure
|
|
199
|
+
|
|
200
|
+
For complex applications, organize by business domain rather than technical layer.
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
ecommerce/
|
|
204
|
+
├── users/
|
|
205
|
+
│ ├── models.py
|
|
206
|
+
│ ├── services.py
|
|
207
|
+
│ ├── repository.py
|
|
208
|
+
│ └── api.py
|
|
209
|
+
├── orders/
|
|
210
|
+
│ ├── models.py
|
|
211
|
+
│ ├── services.py
|
|
212
|
+
│ ├── repository.py
|
|
213
|
+
│ └── api.py
|
|
214
|
+
└── shared/
|
|
215
|
+
├── database.py
|
|
216
|
+
└── exceptions.py
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## File and Module Naming
|
|
220
|
+
|
|
221
|
+
### Conventions
|
|
222
|
+
|
|
223
|
+
- Use `snake_case` for all file and module names: `user_repository.py`
|
|
224
|
+
- Avoid abbreviations that obscure meaning: `user_repository.py` not `usr_repo.py`
|
|
225
|
+
- Match class names to file names: `UserService` in `user_service.py`
|
|
226
|
+
|
|
227
|
+
### Import Style
|
|
228
|
+
|
|
229
|
+
Use absolute imports for clarity and reliability:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
# Preferred: Absolute imports
|
|
233
|
+
from myproject.services import UserService
|
|
234
|
+
from myproject.models import User
|
|
235
|
+
|
|
236
|
+
# Avoid: Relative imports
|
|
237
|
+
from ..services import UserService
|
|
238
|
+
from . import models
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Relative imports can break when modules are moved or reorganized.
|
|
242
|
+
|
|
243
|
+
## Best Practices Summary
|
|
244
|
+
|
|
245
|
+
1. **Keep files focused** - One concept per file, consider splitting at 300-500 lines (varies by complexity)
|
|
246
|
+
2. **Define `__all__` explicitly** - Make public interfaces clear
|
|
247
|
+
3. **Prefer flat structures** - Add depth only for genuine sub-domains
|
|
248
|
+
4. **Use absolute imports** - More reliable and clearer
|
|
249
|
+
5. **Be consistent** - Apply patterns uniformly across the project
|
|
250
|
+
6. **Match names to content** - File names should describe their purpose
|
|
251
|
+
7. **Separate concerns** - Keep layers distinct and dependencies flowing one direction
|
|
252
|
+
8. **Document your structure** - Include a README explaining the organization
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-resilience
|
|
3
|
+
description: 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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Resilience Patterns
|
|
7
|
+
|
|
8
|
+
Build 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
|
+
|
|
23
|
+
Retry transient errors (network timeouts, temporary service issues). Don't retry permanent errors (invalid credentials, bad requests).
|
|
24
|
+
|
|
25
|
+
### 2. Exponential Backoff
|
|
26
|
+
|
|
27
|
+
Increase wait time between retries to avoid overwhelming recovering services.
|
|
28
|
+
|
|
29
|
+
### 3. Jitter
|
|
30
|
+
|
|
31
|
+
Add randomness to backoff to prevent thundering herd when many clients retry simultaneously.
|
|
32
|
+
|
|
33
|
+
### 4. Bounded Retries
|
|
34
|
+
|
|
35
|
+
Cap both attempt count and total duration to prevent infinite retry loops.
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from 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
|
+
)
|
|
46
|
+
def 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
|
+
|
|
54
|
+
Use the `tenacity` library for production-grade retry logic. For simpler cases, consider built-in retry functionality or a lightweight custom implementation.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from tenacity import (
|
|
58
|
+
retry,
|
|
59
|
+
stop_after_attempt,
|
|
60
|
+
stop_after_delay,
|
|
61
|
+
wait_exponential_jitter,
|
|
62
|
+
retry_if_exception_type,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
TRANSIENT_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
|
+
)
|
|
72
|
+
def fetch_data(url: str) -> dict:
|
|
73
|
+
"""Fetch data with automatic retry on transient failures."""
|
|
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
|
+
|
|
81
|
+
Whitelist 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
|
|
88
|
+
from tenacity import retry, retry_if_exception_type
|
|
89
|
+
import httpx
|
|
90
|
+
|
|
91
|
+
# Define what's retryable
|
|
92
|
+
RETRYABLE_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
|
+
)
|
|
104
|
+
def 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
|
+
|
|
111
|
+
Retry specific HTTP status codes that indicate transient issues.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from tenacity import retry, retry_if_result, stop_after_attempt
|
|
115
|
+
import httpx
|
|
116
|
+
|
|
117
|
+
RETRY_STATUS_CODES = {429, 502, 503, 504}
|
|
118
|
+
|
|
119
|
+
def 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
|
+
)
|
|
128
|
+
def 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
|
+
|
|
135
|
+
Handle both network exceptions and HTTP status codes.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from 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
|
+
)
|
|
146
|
+
import logging
|
|
147
|
+
import httpx
|
|
148
|
+
|
|
149
|
+
logger = logging.getLogger(__name__)
|
|
150
|
+
|
|
151
|
+
TRANSIENT_EXCEPTIONS = (
|
|
152
|
+
ConnectionError,
|
|
153
|
+
TimeoutError,
|
|
154
|
+
httpx.ConnectError,
|
|
155
|
+
httpx.ReadTimeout,
|
|
156
|
+
)
|
|
157
|
+
RETRY_STATUS_CODES = {429, 500, 502, 503, 504}
|
|
158
|
+
|
|
159
|
+
def 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
|
+
)
|
|
171
|
+
def 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
|
+
## Advanced Patterns
|
|
181
|
+
|
|
182
|
+
### Pattern 5: Logging Retry Attempts
|
|
183
|
+
|
|
184
|
+
Track retry behavior for debugging and alerting.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
188
|
+
import structlog
|
|
189
|
+
|
|
190
|
+
logger = structlog.get_logger()
|
|
191
|
+
|
|
192
|
+
def log_retry_attempt(retry_state):
|
|
193
|
+
"""Log detailed retry information."""
|
|
194
|
+
exception = retry_state.outcome.exception()
|
|
195
|
+
logger.warning(
|
|
196
|
+
"Retrying operation",
|
|
197
|
+
attempt=retry_state.attempt_number,
|
|
198
|
+
exception_type=type(exception).__name__,
|
|
199
|
+
exception_message=str(exception),
|
|
200
|
+
next_wait_seconds=retry_state.next_action.sleep if retry_state.next_action else None,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
@retry(
|
|
204
|
+
stop=stop_after_attempt(3),
|
|
205
|
+
wait=wait_exponential(multiplier=1, max=10),
|
|
206
|
+
before_sleep=log_retry_attempt,
|
|
207
|
+
)
|
|
208
|
+
def call_with_logging(request: dict) -> dict:
|
|
209
|
+
"""External call with retry logging."""
|
|
210
|
+
...
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Pattern 6: Timeout Decorator
|
|
214
|
+
|
|
215
|
+
Create reusable timeout decorators for consistent timeout handling.
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
import asyncio
|
|
219
|
+
from functools import wraps
|
|
220
|
+
from typing import TypeVar, Callable
|
|
221
|
+
|
|
222
|
+
T = TypeVar("T")
|
|
223
|
+
|
|
224
|
+
def with_timeout(seconds: float):
|
|
225
|
+
"""Decorator to add timeout to async functions."""
|
|
226
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
227
|
+
@wraps(func)
|
|
228
|
+
async def wrapper(*args, **kwargs) -> T:
|
|
229
|
+
return await asyncio.wait_for(
|
|
230
|
+
func(*args, **kwargs),
|
|
231
|
+
timeout=seconds,
|
|
232
|
+
)
|
|
233
|
+
return wrapper
|
|
234
|
+
return decorator
|
|
235
|
+
|
|
236
|
+
@with_timeout(30)
|
|
237
|
+
async def fetch_with_timeout(url: str) -> dict:
|
|
238
|
+
"""Fetch URL with 30 second timeout."""
|
|
239
|
+
async with httpx.AsyncClient() as client:
|
|
240
|
+
response = await client.get(url)
|
|
241
|
+
return response.json()
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Pattern 7: Cross-Cutting Concerns via Decorators
|
|
245
|
+
|
|
246
|
+
Stack decorators to separate infrastructure from business logic.
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from functools import wraps
|
|
250
|
+
from typing import TypeVar, Callable
|
|
251
|
+
import structlog
|
|
252
|
+
|
|
253
|
+
logger = structlog.get_logger()
|
|
254
|
+
T = TypeVar("T")
|
|
255
|
+
|
|
256
|
+
def traced(name: str | None = None):
|
|
257
|
+
"""Add tracing to function calls."""
|
|
258
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
259
|
+
span_name = name or func.__name__
|
|
260
|
+
|
|
261
|
+
@wraps(func)
|
|
262
|
+
async def wrapper(*args, **kwargs) -> T:
|
|
263
|
+
logger.info("Operation started", operation=span_name)
|
|
264
|
+
try:
|
|
265
|
+
result = await func(*args, **kwargs)
|
|
266
|
+
logger.info("Operation completed", operation=span_name)
|
|
267
|
+
return result
|
|
268
|
+
except Exception as e:
|
|
269
|
+
logger.error("Operation failed", operation=span_name, error=str(e))
|
|
270
|
+
raise
|
|
271
|
+
return wrapper
|
|
272
|
+
return decorator
|
|
273
|
+
|
|
274
|
+
# Stack multiple concerns
|
|
275
|
+
@traced("fetch_user_data")
|
|
276
|
+
@with_timeout(30)
|
|
277
|
+
@retry(stop=stop_after_attempt(3), wait=wait_exponential_jitter())
|
|
278
|
+
async def fetch_user_data(user_id: str) -> dict:
|
|
279
|
+
"""Fetch user with tracing, timeout, and retry."""
|
|
280
|
+
...
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Pattern 8: Dependency Injection for Testability
|
|
284
|
+
|
|
285
|
+
Pass infrastructure components through constructors for easy testing.
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
from dataclasses import dataclass
|
|
289
|
+
from typing import Protocol
|
|
290
|
+
|
|
291
|
+
class Logger(Protocol):
|
|
292
|
+
def info(self, msg: str, **kwargs) -> None: ...
|
|
293
|
+
def error(self, msg: str, **kwargs) -> None: ...
|
|
294
|
+
|
|
295
|
+
class MetricsClient(Protocol):
|
|
296
|
+
def increment(self, metric: str, tags: dict | None = None) -> None: ...
|
|
297
|
+
def timing(self, metric: str, value: float) -> None: ...
|
|
298
|
+
|
|
299
|
+
@dataclass
|
|
300
|
+
class UserService:
|
|
301
|
+
"""Service with injected infrastructure."""
|
|
302
|
+
|
|
303
|
+
repository: UserRepository
|
|
304
|
+
logger: Logger
|
|
305
|
+
metrics: MetricsClient
|
|
306
|
+
|
|
307
|
+
async def get_user(self, user_id: str) -> User:
|
|
308
|
+
self.logger.info("Fetching user", user_id=user_id)
|
|
309
|
+
start = time.perf_counter()
|
|
310
|
+
|
|
311
|
+
try:
|
|
312
|
+
user = await self.repository.get(user_id)
|
|
313
|
+
self.metrics.increment("user.fetch.success")
|
|
314
|
+
return user
|
|
315
|
+
except Exception as e:
|
|
316
|
+
self.metrics.increment("user.fetch.error")
|
|
317
|
+
self.logger.error("Failed to fetch user", user_id=user_id, error=str(e))
|
|
318
|
+
raise
|
|
319
|
+
finally:
|
|
320
|
+
elapsed = time.perf_counter() - start
|
|
321
|
+
self.metrics.timing("user.fetch.duration", elapsed)
|
|
322
|
+
|
|
323
|
+
# Easy to test with fakes
|
|
324
|
+
service = UserService(
|
|
325
|
+
repository=FakeRepository(),
|
|
326
|
+
logger=FakeLogger(),
|
|
327
|
+
metrics=FakeMetrics(),
|
|
328
|
+
)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Pattern 9: Fail-Safe Defaults
|
|
332
|
+
|
|
333
|
+
Degrade gracefully when non-critical operations fail.
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
from typing import TypeVar
|
|
337
|
+
from collections.abc import Callable
|
|
338
|
+
|
|
339
|
+
T = TypeVar("T")
|
|
340
|
+
|
|
341
|
+
def fail_safe(default: T, log_failure: bool = True):
|
|
342
|
+
"""Return default value on failure instead of raising."""
|
|
343
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
344
|
+
@wraps(func)
|
|
345
|
+
async def wrapper(*args, **kwargs) -> T:
|
|
346
|
+
try:
|
|
347
|
+
return await func(*args, **kwargs)
|
|
348
|
+
except Exception as e:
|
|
349
|
+
if log_failure:
|
|
350
|
+
logger.warning(
|
|
351
|
+
"Operation failed, using default",
|
|
352
|
+
function=func.__name__,
|
|
353
|
+
error=str(e),
|
|
354
|
+
)
|
|
355
|
+
return default
|
|
356
|
+
return wrapper
|
|
357
|
+
return decorator
|
|
358
|
+
|
|
359
|
+
@fail_safe(default=[])
|
|
360
|
+
async def get_recommendations(user_id: str) -> list[str]:
|
|
361
|
+
"""Get recommendations, return empty list on failure."""
|
|
362
|
+
...
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Best Practices Summary
|
|
366
|
+
|
|
367
|
+
1. **Retry only transient errors** - Don't retry bugs or authentication failures
|
|
368
|
+
2. **Use exponential backoff** - Give services time to recover
|
|
369
|
+
3. **Add jitter** - Prevent thundering herd from synchronized retries
|
|
370
|
+
4. **Cap total duration** - `stop_after_attempt(5) | stop_after_delay(60)`
|
|
371
|
+
5. **Log every retry** - Silent retries hide systemic problems
|
|
372
|
+
6. **Use decorators** - Keep retry logic separate from business logic
|
|
373
|
+
7. **Inject dependencies** - Make infrastructure testable
|
|
374
|
+
8. **Set timeouts everywhere** - Every network call needs a timeout
|
|
375
|
+
9. **Fail gracefully** - Return cached/default values for non-critical paths
|
|
376
|
+
10. **Monitor retry rates** - High retry rates indicate underlying issues
|