python-skills 1.0.0__py3-none-any.whl

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.
Files changed (105) hide show
  1. python_skills/__init__.py +10 -0
  2. python_skills/__main__.py +6 -0
  3. python_skills/adapters/__init__.py +48 -0
  4. python_skills/adapters/agent_skills.py +415 -0
  5. python_skills/adapters/aider_adapter.py +226 -0
  6. python_skills/adapters/base.py +153 -0
  7. python_skills/adapters/claude.py +474 -0
  8. python_skills/adapters/cline.py +332 -0
  9. python_skills/adapters/codex.py +24 -0
  10. python_skills/adapters/continue_adapter.py +198 -0
  11. python_skills/adapters/cursor.py +327 -0
  12. python_skills/adapters/gemini.py +26 -0
  13. python_skills/adapters/goose.py +26 -0
  14. python_skills/adapters/junie.py +25 -0
  15. python_skills/adapters/kiro.py +382 -0
  16. python_skills/adapters/opencode.py +27 -0
  17. python_skills/adapters/roo.py +25 -0
  18. python_skills/adapters/universal.py +203 -0
  19. python_skills/adapters/vscode.py +27 -0
  20. python_skills/adapters/windsurf.py +26 -0
  21. python_skills/adapters/zed.py +27 -0
  22. python_skills/cli.py +326 -0
  23. python_skills/config.py +160 -0
  24. python_skills/detector.py +152 -0
  25. python_skills/installer.py +163 -0
  26. python_skills/markers.py +115 -0
  27. python_skills/skills/__init__.py +14 -0
  28. python_skills/skills/loader.py +171 -0
  29. python_skills/skills/metadata.py +152 -0
  30. python_skills/skills/registry.py +101 -0
  31. python_skills/state.py +204 -0
  32. python_skills-1.0.0.dist-info/METADATA +99 -0
  33. python_skills-1.0.0.dist-info/RECORD +105 -0
  34. python_skills-1.0.0.dist-info/WHEEL +4 -0
  35. python_skills-1.0.0.dist-info/entry_points.txt +2 -0
  36. python_skills-1.0.0.dist-info/licenses/LICENSE +21 -0
  37. skills/advanced_python.md +239 -0
  38. skills/anti_patterns/index.md +406 -0
  39. skills/comprehensions.md +167 -0
  40. skills/control_flow.md +175 -0
  41. skills/data_structures.md +243 -0
  42. skills/debugging/common_bugs.md +222 -0
  43. skills/debugging/inspection_techniques.md +249 -0
  44. skills/debugging/root_cause.md +203 -0
  45. skills/engineering/application_logging.md +195 -0
  46. skills/engineering/cli_apps.md +207 -0
  47. skills/engineering/configuration.md +218 -0
  48. skills/engineering/database.md +240 -0
  49. skills/engineering/dependency_management.md +205 -0
  50. skills/engineering/http_clients.md +267 -0
  51. skills/engineering/modules_packages.md +211 -0
  52. skills/engineering/packaging.md +197 -0
  53. skills/engineering/project_structure.md +155 -0
  54. skills/engineering/pyproject_toml.md +302 -0
  55. skills/engineering/virtual_environments.md +206 -0
  56. skills/functions.md +244 -0
  57. skills/generation/async_concurrency.md +291 -0
  58. skills/generation/error_handling.md +276 -0
  59. skills/generation/protocols_generics.md +243 -0
  60. skills/generation/type_hints.md +290 -0
  61. skills/generation/validation_pipeline.md +274 -0
  62. skills/generation/workflow.md +190 -0
  63. skills/oop.md +228 -0
  64. skills/quality/abstractions.md +154 -0
  65. skills/quality/comments.md +177 -0
  66. skills/quality/documentation.md +176 -0
  67. skills/quality/duplication.md +137 -0
  68. skills/quality/maintainability.md +142 -0
  69. skills/quality/naming.md +171 -0
  70. skills/quality/quality_functions.md +245 -0
  71. skills/quality/readability.md +239 -0
  72. skills/quality/type_annotations.md +192 -0
  73. skills/refactoring/behavior_preservation.md +157 -0
  74. skills/refactoring/incremental.md +187 -0
  75. skills/refactoring/interface_stability.md +199 -0
  76. skills/refactoring/safe_refactoring.md +206 -0
  77. skills/security/auth_boundaries.md +200 -0
  78. skills/security/command_injection.md +207 -0
  79. skills/security/dependency_risks.md +282 -0
  80. skills/security/file_handling.md +156 -0
  81. skills/security/input_validation.md +190 -0
  82. skills/security/path_traversal.md +172 -0
  83. skills/security/secrets.md +171 -0
  84. skills/security/sql_injection.md +188 -0
  85. skills/security/unsafe_deserialization.md +164 -0
  86. skills/stdlib/argparse.md +178 -0
  87. skills/stdlib/collections.md +212 -0
  88. skills/stdlib/datetime.md +187 -0
  89. skills/stdlib/functools.md +238 -0
  90. skills/stdlib/itertools.md +183 -0
  91. skills/stdlib/json.md +162 -0
  92. skills/stdlib/logging.md +185 -0
  93. skills/stdlib/os_sys.md +184 -0
  94. skills/stdlib/pathlib.md +218 -0
  95. skills/stdlib/re.md +171 -0
  96. skills/stdlib/statistics.md +112 -0
  97. skills/stdlib/subprocess.md +211 -0
  98. skills/testing/async_tests.md +249 -0
  99. skills/testing/coverage.md +168 -0
  100. skills/testing/edge_cases.md +197 -0
  101. skills/testing/fixtures_mocks.md +203 -0
  102. skills/testing/organization.md +205 -0
  103. skills/testing/parameterized.md +174 -0
  104. skills/testing/regression_tests.md +165 -0
  105. skills/variables_types.md +107 -0
@@ -0,0 +1,240 @@
1
+ ---
2
+ name: engineering_database
3
+ purpose: Database access patterns and best practices
4
+ category: engineering
5
+ triggers:
6
+ - database
7
+ - sql
8
+ - query
9
+ - orm
10
+ - sqlite
11
+ - postgresql
12
+ - mysql
13
+ - migration
14
+ dependencies:
15
+ - security/sql_injection.md
16
+ - engineering/configuration.md
17
+ - generation/async_concurrency.md
18
+ - generation/error_handling.md
19
+ - testing/organization.md
20
+ priority: primary
21
+ estimated_tokens: 2200
22
+ ---
23
+ # Engineering: Database
24
+
25
+ **Purpose**: Database access patterns and best practices.
26
+
27
+ **When to use**: Any code interacting with databases.
28
+
29
+ ---
30
+
31
+ ## Core Rules
32
+
33
+ ### Driver Selection
34
+ | Database | Async Driver | Sync Driver |
35
+ |----------|--------------|-------------|
36
+ | PostgreSQL | `asyncpg` | `psycopg` (v3) |
37
+ | MySQL | `aiomysql` | `pymysql` |
38
+ | SQLite | `aiosqlite` | `sqlite3` (stdlib) |
39
+ | MongoDB | `motor` | `pymongo` |
40
+
41
+ ### Connection Management
42
+ ```python
43
+ # Async (asyncpg example)
44
+ import asyncpg
45
+
46
+ pool: asyncpg.Pool = await asyncpg.create_pool(
47
+ dsn="postgresql://user:pass@host/db",
48
+ min_size=5,
49
+ max_size=20,
50
+ command_timeout=30,
51
+ )
52
+
53
+ async def get_user(user_id: int) -> User | None:
54
+ async with pool.acquire() as conn:
55
+ row = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
56
+ return User(**row) if row else None
57
+
58
+ # Sync (psycopg)
59
+ import psycopg
60
+ from psycopg_pool import ConnectionPool
61
+
62
+ pool = ConnectionPool("postgresql://user:pass@host/db", min_size=5, max_size=20)
63
+
64
+ def get_user(user_id: int) -> User | None:
65
+ with pool.connection() as conn:
66
+ with conn.cursor() as cur:
67
+ cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
68
+ row = cur.fetchone()
69
+ return User(*row) if row else None
70
+ ```
71
+
72
+ ### Parameterized Queries (SQL Injection Prevention)
73
+ ```python
74
+ # CORRECT — parameterized
75
+ cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
76
+ cursor.execute("SELECT * FROM users WHERE id = $1", (user_id,)) # asyncpg
77
+
78
+ # WRONG — string interpolation
79
+ cursor.execute(f"SELECT * FROM users WHERE email = '{email}'") # INJECTION!
80
+ cursor.execute("SELECT * FROM users WHERE id = " + str(user_id)) # INJECTION!
81
+ ```
82
+
83
+ ### ORM (SQLAlchemy 2.0+)
84
+ ```python
85
+ from sqlalchemy import select
86
+ from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
87
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
88
+
89
+ class Base(DeclarativeBase):
90
+ pass
91
+
92
+ class User(Base):
93
+ __tablename__ = "users"
94
+ id: Mapped[int] = mapped_column(primary_key=True)
95
+ email: Mapped[str] = mapped_column(unique=True)
96
+ name: Mapped[str]
97
+
98
+ engine = create_async_engine("postgresql+asyncpg://user:pass@host/db")
99
+ async_session = async_sessionmaker(engine, expire_on_commit=False)
100
+
101
+ async def get_user(session: AsyncSession, user_id: int) -> User | None:
102
+ return await session.get(User, user_id)
103
+
104
+ async def create_user(session: AsyncSession, email: str, name: str) -> User:
105
+ user = User(email=email, name=name)
106
+ session.add(user)
107
+ await session.commit()
108
+ return user
109
+ ```
110
+
111
+ ### Migrations (Alembic)
112
+ ```bash
113
+ # Initialize
114
+ alembic init alembic
115
+
116
+ # Create migration
117
+ alembic revision --autogenerate -m "Add users table"
118
+
119
+ # Apply
120
+ alembic upgrade head
121
+ ```
122
+
123
+ ### Repository Pattern
124
+ ```python
125
+ from abc import ABC, abstractmethod
126
+ from typing import Protocol
127
+
128
+ class UserRepository(Protocol):
129
+ async def get(self, user_id: int) -> User | None: ...
130
+ async def get_by_email(self, email: str) -> User | None: ...
131
+ async def save(self, user: User) -> User: ...
132
+ async def delete(self, user_id: int) -> bool: ...
133
+
134
+ class PostgresUserRepository:
135
+ def __init__(self, pool: asyncpg.Pool):
136
+ self.pool = pool
137
+
138
+ async def get(self, user_id: int) -> User | None:
139
+ async with self.pool.acquire() as conn:
140
+ row = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
141
+ return User(**row) if row else None
142
+
143
+ # ... implement other methods
144
+
145
+ # Usage (dependency injection)
146
+ async def handler(repo: UserRepository, user_id: int):
147
+ user = await repo.get(user_id)
148
+ ```
149
+
150
+ ### Transactions
151
+ ```python
152
+ # Async
153
+ async with pool.acquire() as conn:
154
+ async with conn.transaction():
155
+ await conn.execute("INSERT ...")
156
+ await conn.execute("UPDATE ...")
157
+
158
+ # SQLAlchemy
159
+ async with async_session() as session:
160
+ async with session.begin():
161
+ session.add(user1)
162
+ session.add(user2)
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Decision Rules
168
+
169
+ | Need | Approach |
170
+ |------|----------|
171
+ | Simple queries, performance critical | Raw driver (`asyncpg`, `psycopg`) |
172
+ | Complex domain model, migrations | SQLAlchemy + Alembic |
173
+ | Document data | MongoDB (`motor`/`pymongo`) |
174
+ | Embedded/local | SQLite (`aiosqlite`/`sqlite3`) |
175
+ | Testing | In-memory SQLite or testcontainers |
176
+
177
+ ---
178
+
179
+ ## Preferred Patterns
180
+
181
+ ```python
182
+ # Dependency injection for testability
183
+ class Database:
184
+ def __init__(self, pool: asyncpg.Pool):
185
+ self.pool = pool
186
+ self.users = PostgresUserRepository(pool)
187
+ self.orders = PostgresOrderRepository(pool)
188
+
189
+ # Context manager for transactions
190
+ @asynccontextmanager
191
+ async def transaction(db: Database):
192
+ async with db.pool.acquire() as conn:
193
+ async with conn.transaction():
194
+ yield conn
195
+
196
+ # Type-safe queries with dataclasses
197
+ from dataclasses import dataclass
198
+
199
+ @dataclass
200
+ class UserRow:
201
+ id: int
202
+ email: str
203
+ name: str
204
+
205
+ async def get_user(conn, user_id: int) -> UserRow | None:
206
+ row = await conn.fetchrow("SELECT id, email, name FROM users WHERE id = $1", user_id)
207
+ return UserRow(**row) if row else None
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Avoid
213
+
214
+ - String interpolation in SQL (injection!)
215
+ - Global connection/pool (use DI)
216
+ - Long-running transactions
217
+ - N+1 queries (use joins or batch loading)
218
+ - ORM for everything (raw SQL for complex queries)
219
+ - No connection pooling
220
+ - Committing in loops
221
+
222
+ ---
223
+
224
+ ## Validation Considerations
225
+
226
+ - Test with real database (testcontainers)
227
+ - Verify parameterized queries used everywhere
228
+ - Check connection pool sizing
229
+ - Test migration up/down
230
+ - Load test connection limits
231
+
232
+ ---
233
+
234
+ ## Related Skills
235
+
236
+ - `security/sql_injection.md`
237
+ - `engineering/configuration.md`
238
+ - `generation/async_concurrency.md`
239
+ - `generation/error_handling.md`
240
+ - `testing/organization.md`
@@ -0,0 +1,205 @@
1
+ # Engineering: Dependency Management
2
+
3
+ **Purpose**: Rules for adding, updating, and managing dependencies.
4
+
5
+ **When to use**: Any time a new import or requirement is considered.
6
+
7
+ ---
8
+
9
+ ## Core Rules
10
+
11
+ ### Dependency Decision Tree
12
+ ```
13
+ Can stdlib solve this?
14
+ ↓ YES → Use stdlib
15
+ ↓ NO
16
+ Is an existing project dependency suitable?
17
+ ↓ YES → Use existing
18
+ ↓ NO
19
+ Is a new dependency justified?
20
+ ↓ YES → Add with justification
21
+ ↓ NO → Reconsider approach
22
+ ```
23
+
24
+ ### Justification Criteria for New Dependencies
25
+ 1. **Functionality gap** — stdlib + existing deps cannot reasonably solve
26
+ 2. **Maintenance** — Actively maintained (recent releases, responsive issues)
27
+ 3. **Security** — No known unpatched vulnerabilities
28
+ 4. **Compatibility** — Supports project's Python version range
29
+ 5. **License** — Compatible with project license
30
+ 6. **Size** — Reasonable install size, minimal transitive deps
31
+ 7. **Alternatives evaluated** — Considered lighter alternatives
32
+
33
+ ### Version Constraints
34
+ ```toml
35
+ # Libraries: compatible ranges
36
+ dependencies = [
37
+ "requests>=2.31,<3", # Semver major
38
+ "pydantic>=2.0,<3",
39
+ "click>=8.0,<9",
40
+ ]
41
+
42
+ # Applications: lock files
43
+ # requirements.txt / uv.lock / poetry.lock / pdm.lock
44
+ ```
45
+
46
+ ### Dependency Categories
47
+ | Category | Purpose | Version Policy |
48
+ |----------|---------|----------------|
49
+ | Runtime | Required for library/app to work | Compatible range |
50
+ | Optional | Extra features | Compatible range |
51
+ | Dev | Testing, linting, docs | Pinned or loose |
52
+ | Build | Build backend, generators | Pinned |
53
+
54
+ ---
55
+
56
+ ## Lock Files (Applications)
57
+
58
+ ### For Applications (Not Libraries)
59
+ ```bash
60
+ # uv (fast, modern)
61
+ uv pip compile pyproject.toml -o requirements.txt
62
+ uv sync
63
+
64
+ # pip-tools
65
+ pip-compile pyproject.toml -o requirements.txt
66
+ pip-sync requirements.txt
67
+
68
+ # poetry
69
+ poetry lock
70
+ poetry install
71
+
72
+ # pdm
73
+ pdm lock
74
+ pdm install
75
+ ```
76
+
77
+ ### Lock File Policy
78
+ - **Libraries**: No lock file in repo (only `pyproject.toml`)
79
+ - **Applications**: Lock file committed (`requirements.txt`, `uv.lock`, `poetry.lock`)
80
+ - **CI**: Use lock file for reproducible builds
81
+
82
+ ---
83
+
84
+ ## Dependency Security
85
+
86
+ ### Scanning
87
+ ```bash
88
+ # Safety (checks known vulnerabilities)
89
+ safety check
90
+
91
+ # pip-audit
92
+ pip-audit
93
+
94
+ # GitHub Dependabot / GitLab Dependency Scanning
95
+ # Configured in CI
96
+ ```
97
+
98
+ ### Update Policy
99
+ ```bash
100
+ # Check outdated
101
+ uv pip list --outdated
102
+ pip list --outdated
103
+
104
+ # Update (test first!)
105
+ uv pip install --upgrade package
106
+ # Run full test suite
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Vendoring (Last Resort)
112
+ ```python
113
+ # Only when:
114
+ # - Dependency abandoned
115
+ # - Need modification
116
+ # - Cannot depend on external (air-gapped)
117
+
118
+ # Structure
119
+ src/
120
+ └── mypackage/
121
+ ├── _vendor/
122
+ │ └── requests/ # Copied source
123
+ │ └── __init__.py # from _vendor.requests import ...
124
+ └── ...
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Decision Rules
130
+
131
+ | Situation | Action |
132
+ |-----------|--------|
133
+ | Stdlib has it | Use stdlib |
134
+ | Existing dep has it | Use existing |
135
+ | Simple utility (few lines) | Copy code (with license) |
136
+ | Complex, well-maintained lib | Add dependency |
137
+ | Abandoned dependency | Fork/vendor or replace |
138
+ | Security vulnerability | Upgrade immediately |
139
+ | License conflict | Find alternative |
140
+
141
+ ---
142
+
143
+ ## Preferred Patterns
144
+
145
+ ```toml
146
+ # pyproject.toml - clear separation
147
+ [project]
148
+ dependencies = [
149
+ # Core runtime deps
150
+ "httpx>=0.25,<1",
151
+ "pydantic>=2.0,<3",
152
+ "python-dotenv>=1.0,<2",
153
+ ]
154
+
155
+ [project.optional-dependencies]
156
+ # Optional features
157
+ async = ["aiohttp>=3.8,<4"]
158
+ redis = ["redis>=5.0,<6"]
159
+ postgres = ["asyncpg>=0.29,<1", "sqlalchemy>=2.0,<3"]
160
+
161
+ # Dev tools (not installed by users)
162
+ dev = [
163
+ "pytest>=7.4,<8",
164
+ "pytest-asyncio>=0.21,<1",
165
+ "pytest-cov>=4.1,<5",
166
+ "ruff>=0.5,<1",
167
+ "mypy>=1.10,<2",
168
+ "pre-commit>=3.0,<4",
169
+ ]
170
+
171
+ # Build deps (in build-system)
172
+ [build-system]
173
+ requires = ["hatchling"]
174
+ build-backend = "hatchling.build"
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Avoid
180
+
181
+ - Adding deps for trivial functionality (`left-pad` style)
182
+ - Pinning exact versions in library `pyproject.toml` (`==1.2.3`)
183
+ - No upper bounds in library deps (breaking changes)
184
+ - Commiting `requirements.txt` for libraries
185
+ - Ignoring security advisories
186
+ - Transitive dependency hell (audit `pipdeptree`)
187
+
188
+ ---
189
+
190
+ ## Validation Considerations
191
+
192
+ - `pipdeptree` shows dependency graph
193
+ - `pip check` validates consistency
194
+ - `safety check` / `pip-audit` clean
195
+ - License check: `pip-licenses`
196
+ - Build reproducibility: `pip install` from lock file
197
+
198
+ ---
199
+
200
+ ## Related Skills
201
+
202
+ - `engineering/pyproject_toml.md`
203
+ - `engineering/packaging.md`
204
+ - `engineering/virtual_environments.md`
205
+ - `security/dependency_risks.md`
@@ -0,0 +1,267 @@
1
+ ---
2
+ name: engineering_http_clients
3
+ purpose: HTTP client selection and usage patterns
4
+ category: engineering
5
+ triggers:
6
+ - http
7
+ - request
8
+ - client
9
+ - api
10
+ - rest
11
+ - webhook
12
+ - download
13
+ dependencies:
14
+ - generation/async_concurrency.md
15
+ - generation/error_handling.md
16
+ - engineering/configuration.md
17
+ - security/secrets.md
18
+ - testing/organization.md
19
+ priority: primary
20
+ estimated_tokens: 2500
21
+ ---
22
+ # Engineering: HTTP Clients
23
+
24
+ **Purpose**: HTTP client selection and usage patterns.
25
+
26
+ **When to use**: Making HTTP requests (APIs, webhooks, downloads).
27
+
28
+ ---
29
+
30
+ ## Core Rules
31
+
32
+ ### Client Selection
33
+
34
+ | Need | Client |
35
+ |------|--------|
36
+ | Sync, simple | `requests` |
37
+ | Sync + HTTP/2 | `httpx` (sync) |
38
+ | Async | `httpx` / `aiohttp` |
39
+ | Stdlib only | `urllib.request` (avoid) |
40
+ | Testing | `respx` / `pytest-httpx` / `requests-mock` |
41
+
42
+ ### httpx (Recommended — Sync + Async)
43
+ ```python
44
+ import httpx
45
+
46
+ # Sync
47
+ client = httpx.Client(timeout=30.0, limits=httpx.Limits(max_connections=100))
48
+ response = client.get("https://api.example.com/users")
49
+ client.close()
50
+
51
+ # Context manager
52
+ with httpx.Client() as client:
53
+ response = client.get(url)
54
+
55
+ # Async
56
+ async with httpx.AsyncClient() as client:
57
+ response = await client.get(url)
58
+
59
+ # Common options
60
+ client = httpx.Client(
61
+ base_url="https://api.example.com",
62
+ headers={"User-Agent": "MyApp/1.0"},
63
+ timeout=httpx.Timeout(connect=5.0, read=30.0),
64
+ follow_redirects=True,
65
+ limits=httpx.Limits(max_keepalive_connections=20),
66
+ )
67
+ ```
68
+
69
+ ### Response Handling
70
+ ```python
71
+ response = client.get(url)
72
+
73
+ # Status
74
+ response.status_code
75
+ response.is_success
76
+ response.raise_for_status() # Raises HTTPStatusError
77
+
78
+ # Content
79
+ response.text # str (decoded)
80
+ response.content # bytes
81
+ response.json() # Parsed JSON
82
+ response.iter_bytes() # Stream
83
+
84
+ # Headers
85
+ response.headers["Content-Type"]
86
+ response.headers.get("X-Custom")
87
+ ```
88
+
89
+ ### Error Handling
90
+ ```python
91
+ import httpx
92
+
93
+ try:
94
+ response = client.get(url, timeout=10.0)
95
+ response.raise_for_status()
96
+ except httpx.TimeoutException:
97
+ logger.warning("Request timed out")
98
+ raise ServiceTimeout()
99
+ except httpx.ConnectError:
100
+ logger.warning("Connection failed")
101
+ raise ServiceUnavailable()
102
+ except httpx.HTTPStatusError as e:
103
+ if e.response.status_code == 404:
104
+ raise NotFoundError()
105
+ elif e.response.status_code == 429:
106
+ raise RateLimited(retry_after=e.response.headers.get("Retry-After"))
107
+ else:
108
+ logger.error("HTTP error", status=e.response.status_code)
109
+ raise ServiceError()
110
+ ```
111
+
112
+ ### Retry Logic
113
+ ```python
114
+ from httpx import Retry
115
+ from httpx._transports.default import RetryStrategy
116
+
117
+ # Built-in retry (httpx 0.25+)
118
+ transport = httpx.HTTPTransport(
119
+ retries=Retry(
120
+ total=3,
121
+ backoff_factor=0.5,
122
+ status_forcelist=[429, 500, 502, 503, 504],
123
+ allowed_methods=["HEAD", "GET", "OPTIONS"],
124
+ )
125
+ )
126
+ client = httpx.Client(transport=transport)
127
+
128
+ # Or use tenacity for complex retry
129
+ from tenacity import retry, stop_after_attempt, wait_exponential
130
+
131
+ @retry(
132
+ wait=wait_exponential(multiplier=1, min=1, max=10),
133
+ stop=stop_after_attempt(3),
134
+ retry=retry_if_exception_type(httpx.RequestError),
135
+ )
136
+ async def fetch_with_retry(client: httpx.AsyncClient, url: str) -> httpx.Response:
137
+ return await client.get(url)
138
+ ```
139
+
140
+ ### Authentication
141
+ ```python
142
+ # Bearer token
143
+ headers = {"Authorization": f"Bearer {token}"}
144
+
145
+ # Basic auth
146
+ auth = httpx.BasicAuth("user", "pass")
147
+
148
+ # Custom auth
149
+ class APIKeyAuth(httpx.Auth):
150
+ def __init__(self, api_key: str):
151
+ self.api_key = api_key
152
+
153
+ def auth_flow(self, request):
154
+ request.headers["X-API-Key"] = self.api_key
155
+ yield request
156
+
157
+ client = httpx.Client(auth=APIKeyAuth("key"))
158
+ ```
159
+
160
+ ### Streaming Large Responses
161
+ ```python
162
+ # Download large file
163
+ with client.stream("GET", url) as response:
164
+ response.raise_for_status()
165
+ with Path("large_file").open("wb") as f:
166
+ for chunk in response.iter_bytes(chunk_size=8192):
167
+ f.write(chunk)
168
+
169
+ # Async streaming
170
+ async with client.stream("GET", url) as response:
171
+ async for chunk in response.aiter_bytes():
172
+ process(chunk)
173
+ ```
174
+
175
+ ### Testing
176
+ ```python
177
+ import pytest
178
+ import respx
179
+ import httpx
180
+
181
+ @respx.mock
182
+ async def test_api():
183
+ respx.get("https://api.example.com/users").mock(
184
+ return_value=httpx.Response(200, json=[{"id": 1}])
185
+ )
186
+
187
+ async with httpx.AsyncClient() as client:
188
+ resp = await client.get("https://api.example.com/users")
189
+ assert resp.json() == [{"id": 1}]
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Decision Rules
195
+
196
+ | Situation | Pattern |
197
+ |-----------|---------|
198
+ | Simple sync requests | `httpx.Client()` |
199
+ | High concurrency sync | `httpx.Client` with limits |
200
+ | Async application | `httpx.AsyncClient` |
201
+ | Complex retry/backoff | `tenacity` + `httpx` |
202
+ | WebSocket | `httpx` (experimental) or `websockets` |
203
+ | Testing | `respx` (async) or `requests-mock` (sync) |
204
+
205
+ ---
206
+
207
+ ## Preferred Patterns
208
+
209
+ ```python
210
+ # Reusable client factory
211
+ def create_client(
212
+ base_url: str,
213
+ timeout: float = 30.0,
214
+ api_key: str | None = None,
215
+ ) -> httpx.Client:
216
+ headers = {"User-Agent": "MyApp/1.0"}
217
+ if api_key:
218
+ headers["Authorization"] = f"Bearer {api_key}"
219
+
220
+ return httpx.Client(
221
+ base_url=base_url,
222
+ headers=headers,
223
+ timeout=httpx.Timeout(timeout),
224
+ limits=httpx.Limits(max_connections=50),
225
+ )
226
+
227
+ # Service wrapper
228
+ class APIService:
229
+ def __init__(self, client: httpx.Client):
230
+ self.client = client
231
+
232
+ def get_user(self, user_id: str) -> User:
233
+ resp = self.client.get(f"/users/{user_id}")
234
+ resp.raise_for_status()
235
+ return User.model_validate(resp.json())
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Avoid
241
+
242
+ - Creating new client per request (no connection pooling)
243
+ - No timeout (hangs forever)
244
+ - Ignoring `raise_for_status()`
245
+ - Blocking sync client in async code
246
+ - Logging full response bodies (may contain secrets)
247
+ - Hardcoding URLs (use config + base_url)
248
+
249
+ ---
250
+
251
+ ## Validation Considerations
252
+
253
+ - Test timeout behavior
254
+ - Test retry logic
255
+ - Test auth handling
256
+ - Mock external APIs in tests
257
+ - Check connection pooling works
258
+
259
+ ---
260
+
261
+ ## Related Skills
262
+
263
+ - `generation/async_concurrency.md`
264
+ - `generation/error_handling.md`
265
+ - `engineering/configuration.md`
266
+ - `security/secrets.md`
267
+ - `testing/organization.md`