@caddis/cli 0.0.0 → 0.1.0
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/LICENSE +21 -0
- package/README.md +150 -1
- package/bundles/antigravity-plugin/agents/code-reviewer.md +47 -0
- package/bundles/antigravity-plugin/agents/preflight.md +53 -0
- package/bundles/antigravity-plugin/guard_agy.py +338 -0
- package/bundles/antigravity-plugin/hooks.json +36 -0
- package/bundles/antigravity-plugin/mcp_config.json +8 -0
- package/bundles/antigravity-plugin/mcp_ping_server.py +55 -0
- package/bundles/antigravity-plugin/plugin.json +5 -0
- package/bundles/antigravity-plugin/session_end_agy.py +57 -0
- package/bundles/antigravity-plugin/skills/_registry.md +115 -0
- package/bundles/antigravity-plugin/skills/add-rules/SKILL.md +45 -0
- package/bundles/antigravity-plugin/skills/api-design/SKILL.md +249 -0
- package/bundles/antigravity-plugin/skills/backend-development/SKILL.md +305 -0
- package/bundles/antigravity-plugin/skills/best-practices/SKILL.md +500 -0
- package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +263 -0
- package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +326 -0
- package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +245 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/anti-patterns.md +571 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/before-after-examples.md +1114 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/best-practices-guide.md +513 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/common-workflows.md +692 -0
- package/bundles/antigravity-plugin/skills/best-practices/references/prompt-patterns.md +547 -0
- package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +57 -0
- package/bundles/antigravity-plugin/skills/ci-cd-pipeline/SKILL.md +315 -0
- package/bundles/antigravity-plugin/skills/code-documentation/SKILL.md +271 -0
- package/bundles/antigravity-plugin/skills/code-review/SKILL.md +122 -0
- package/bundles/antigravity-plugin/skills/codebase-audit/SKILL.md +204 -0
- package/bundles/antigravity-plugin/skills/context-curator/SKILL.md +157 -0
- package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +40 -0
- package/bundles/antigravity-plugin/skills/css-architecture/SKILL.md +305 -0
- package/bundles/antigravity-plugin/skills/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
- package/bundles/antigravity-plugin/skills/database-design/SKILL.md +177 -0
- package/bundles/antigravity-plugin/skills/db-diagram/SKILL.md +148 -0
- package/bundles/antigravity-plugin/skills/db-diagram/scripts/sql_to_graph.py +1212 -0
- package/bundles/antigravity-plugin/skills/digress/SKILL.md +61 -0
- package/bundles/antigravity-plugin/skills/draw-io/SKILL.md +162 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/aws-icons.md +677 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/layout-guidelines.md +142 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/troubleshooting.md +118 -0
- package/bundles/antigravity-plugin/skills/draw-io/references/workflows.md +103 -0
- package/bundles/antigravity-plugin/skills/draw-io/scripts/convert-drawio-to-png.sh +25 -0
- package/bundles/antigravity-plugin/skills/draw-io/scripts/find_aws_icon.py +79 -0
- package/bundles/antigravity-plugin/skills/error-handling/SKILL.md +260 -0
- package/bundles/antigravity-plugin/skills/excalidraw-db/SKILL.md +38 -0
- package/bundles/antigravity-plugin/skills/fastapi-dev/SKILL.md +300 -0
- package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +198 -0
- package/bundles/antigravity-plugin/skills/frontend-design/SKILL.md +193 -0
- package/bundles/antigravity-plugin/skills/frontend-design/references/my-tech-stack.md +127 -0
- package/bundles/antigravity-plugin/skills/gh-cli/SKILL.md +195 -0
- package/bundles/antigravity-plugin/skills/git-commit/SKILL.md +285 -0
- package/bundles/antigravity-plugin/skills/golden-plan/SKILL.md +577 -0
- package/bundles/antigravity-plugin/skills/handoff/SKILL.md +100 -0
- package/bundles/antigravity-plugin/skills/implement/SKILL.md +114 -0
- package/bundles/antigravity-plugin/skills/javascript-typescript/SKILL.md +142 -0
- package/bundles/antigravity-plugin/skills/kb/SKILL.md +60 -0
- package/bundles/antigravity-plugin/skills/mermaid-db/SKILL.md +33 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/SKILL.md +236 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/advanced-features.md +556 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/architecture-diagrams.md +192 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/c4-diagrams.md +410 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/class-diagrams.md +361 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/erd-diagrams.md +510 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/flowcharts.md +450 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/sequence-diagrams.md +394 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/troubleshooting.md +335 -0
- package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/workflows.md +418 -0
- package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +68 -0
- package/bundles/antigravity-plugin/skills/mockup/SKILL.md +242 -0
- package/bundles/antigravity-plugin/skills/particle-art/SKILL.md +243 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/canvas-utils.ts +171 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/dot-field.template.tsx +203 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/flow-field.template.tsx +263 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/node-shape.template.tsx +261 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/shape-sampler.ts +281 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/stipple-morph.template.tsx +167 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/stipple.template.tsx +175 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/trail-ghost.template.tsx +266 -0
- package/bundles/antigravity-plugin/skills/particle-art/references/usage-examples.md +320 -0
- package/bundles/antigravity-plugin/skills/playwright/API_REFERENCE.md +653 -0
- package/bundles/antigravity-plugin/skills/playwright/SKILL.md +454 -0
- package/bundles/antigravity-plugin/skills/playwright/lib/helpers.js +441 -0
- package/bundles/antigravity-plugin/skills/playwright/package.json +26 -0
- package/bundles/antigravity-plugin/skills/playwright/run.js +228 -0
- package/bundles/antigravity-plugin/skills/prd/SKILL.md +107 -0
- package/bundles/antigravity-plugin/skills/preflight/SKILL.md +435 -0
- package/bundles/antigravity-plugin/skills/python/SKILL.md +388 -0
- package/bundles/antigravity-plugin/skills/react-best-practices/SKILL.md +269 -0
- package/bundles/antigravity-plugin/skills/react-dev/README.md +404 -0
- package/bundles/antigravity-plugin/skills/react-dev/SKILL.md +459 -0
- package/bundles/antigravity-plugin/skills/react-dev/examples/generic-components.md +579 -0
- package/bundles/antigravity-plugin/skills/react-dev/examples/server-components.md +579 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/event-handlers.md +574 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/hooks.md +456 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/react-19-patterns.md +638 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/react-router.md +1002 -0
- package/bundles/antigravity-plugin/skills/react-dev/references/tanstack-router.md +587 -0
- package/bundles/antigravity-plugin/skills/refactoring/SKILL.md +486 -0
- package/bundles/antigravity-plugin/skills/resume/SKILL.md +36 -0
- package/bundles/antigravity-plugin/skills/security-review/SKILL.md +196 -0
- package/bundles/antigravity-plugin/skills/setup-project-ai/SKILL.md +61 -0
- package/bundles/antigravity-plugin/skills/ship/SKILL.md +107 -0
- package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +103 -0
- package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +102 -0
- package/bundles/antigravity-plugin/skills/skill-creator/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin/skills/skill-creator/SKILL.md +491 -0
- package/bundles/antigravity-plugin/skills/skill-creator/agents/analyzer.md +274 -0
- package/bundles/antigravity-plugin/skills/skill-creator/agents/comparator.md +202 -0
- package/bundles/antigravity-plugin/skills/skill-creator/agents/grader.md +223 -0
- package/bundles/antigravity-plugin/skills/skill-creator/assets/eval_review.html +146 -0
- package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/bundles/antigravity-plugin/skills/skill-creator/references/schemas.md +430 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/__init__.py +0 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/generate_report.py +326 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/improve_description.py +247 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/package_skill.py +136 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_eval.py +310 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_loop.py +328 -0
- package/bundles/antigravity-plugin/skills/skill-creator/scripts/utils.py +47 -0
- package/bundles/antigravity-plugin/skills/sql/SKILL.md +321 -0
- package/bundles/antigravity-plugin/skills/tdd/SKILL.md +37 -0
- package/bundles/antigravity-plugin/skills/tdd-workflow/SKILL.md +188 -0
- package/bundles/antigravity-plugin/skills/technical-writing/SKILL.md +286 -0
- package/bundles/antigravity-plugin/skills/test-strategy/SKILL.md +155 -0
- package/bundles/antigravity-plugin/skills/ui-brief/SKILL.md +84 -0
- package/bundles/antigravity-plugin/skills/ui-review/SKILL.md +176 -0
- package/bundles/antigravity-plugin/skills/ui-review/references/framework-fixes.md +471 -0
- package/bundles/antigravity-plugin/skills/ui-review/references/visual-checklist.md +236 -0
- package/bundles/antigravity-plugin/skills/usage-review/SKILL.md +77 -0
- package/bundles/antigravity-plugin/skills/use-model/SKILL.md +64 -0
- package/bundles/antigravity-plugin/skills/using-git-worktrees/SKILL.md +217 -0
- package/bundles/antigravity-plugin/skills/version/SKILL.md +18 -0
- package/bundles/antigravity-plugin/skills/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
- package/bundles/antigravity-plugin/skills/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
- package/bundles/antigravity-plugin/skills/warm-editorial-ui/SKILL.md +732 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/LICENSE.txt +202 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/SKILL.md +97 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/examples/console_logging.py +35 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/bundles/antigravity-plugin/skills/webapp-testing/scripts/with_server.py +106 -0
- package/bundles/antigravity-plugin/skills/windows-deployment/SKILL.md +880 -0
- package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +384 -0
- package/bundles/antigravity-plugin/statusline-command-agy.sh +91 -0
- package/bundles/antigravity-plugin/warm_start_agy.py +149 -0
- package/bundles/manifest.json +6 -0
- package/dist/cli.js +5363 -0
- package/package.json +61 -4
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-development
|
|
3
|
+
description: Modern Python development with Python 3.12+, Django, FastAPI, async patterns, and production best practices. Use for Python projects, APIs, data processing, or automation scripts.
|
|
4
|
+
source: wshobson/agents
|
|
5
|
+
license: MIT
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Python Development
|
|
9
|
+
|
|
10
|
+
## Project Setup
|
|
11
|
+
|
|
12
|
+
### Modern Python Project Structure
|
|
13
|
+
```
|
|
14
|
+
my-project/
|
|
15
|
+
├── src/
|
|
16
|
+
│ └── my_project/
|
|
17
|
+
│ ├── __init__.py
|
|
18
|
+
│ ├── main.py
|
|
19
|
+
│ └── utils.py
|
|
20
|
+
├── tests/
|
|
21
|
+
│ ├── __init__.py
|
|
22
|
+
│ └── test_main.py
|
|
23
|
+
├── pyproject.toml
|
|
24
|
+
├── README.md
|
|
25
|
+
└── .gitignore
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### pyproject.toml
|
|
29
|
+
```toml
|
|
30
|
+
[project]
|
|
31
|
+
name = "my-project"
|
|
32
|
+
version = "0.1.0"
|
|
33
|
+
requires-python = ">=3.12"
|
|
34
|
+
dependencies = [
|
|
35
|
+
"fastapi>=0.100.0",
|
|
36
|
+
"pydantic>=2.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=7.0",
|
|
42
|
+
"ruff>=0.1.0",
|
|
43
|
+
"mypy>=1.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 88
|
|
48
|
+
select = ["E", "F", "I", "N", "W"]
|
|
49
|
+
|
|
50
|
+
[tool.mypy]
|
|
51
|
+
strict = true
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Type Hints
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from typing import TypeVar, Generic
|
|
58
|
+
from collections.abc import Sequence
|
|
59
|
+
|
|
60
|
+
T = TypeVar('T')
|
|
61
|
+
|
|
62
|
+
def process_items(items: Sequence[str]) -> list[str]:
|
|
63
|
+
return [item.upper() for item in items]
|
|
64
|
+
|
|
65
|
+
class Repository(Generic[T]):
|
|
66
|
+
def get(self, id: int) -> T | None: ...
|
|
67
|
+
def save(self, item: T) -> T: ...
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Async Patterns
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import asyncio
|
|
74
|
+
from collections.abc import AsyncIterator
|
|
75
|
+
|
|
76
|
+
async def fetch_all(urls: list[str]) -> list[dict]:
|
|
77
|
+
async with aiohttp.ClientSession() as session:
|
|
78
|
+
tasks = [fetch_one(session, url) for url in urls]
|
|
79
|
+
return await asyncio.gather(*tasks)
|
|
80
|
+
|
|
81
|
+
async def stream_data() -> AsyncIterator[bytes]:
|
|
82
|
+
async with aiofiles.open('large_file.txt', 'rb') as f:
|
|
83
|
+
async for chunk in f:
|
|
84
|
+
yield chunk
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## FastAPI Patterns
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from fastapi import FastAPI, Depends, HTTPException
|
|
91
|
+
from pydantic import BaseModel
|
|
92
|
+
|
|
93
|
+
app = FastAPI()
|
|
94
|
+
|
|
95
|
+
class UserCreate(BaseModel):
|
|
96
|
+
email: str
|
|
97
|
+
name: str
|
|
98
|
+
|
|
99
|
+
class UserResponse(BaseModel):
|
|
100
|
+
id: int
|
|
101
|
+
email: str
|
|
102
|
+
name: str
|
|
103
|
+
|
|
104
|
+
@app.post("/users", response_model=UserResponse)
|
|
105
|
+
async def create_user(
|
|
106
|
+
user: UserCreate,
|
|
107
|
+
db: Database = Depends(get_db)
|
|
108
|
+
) -> UserResponse:
|
|
109
|
+
result = await db.users.create(user.model_dump())
|
|
110
|
+
return UserResponse(**result)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Testing
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import pytest
|
|
117
|
+
from unittest.mock import AsyncMock, patch
|
|
118
|
+
|
|
119
|
+
@pytest.fixture
|
|
120
|
+
def mock_db():
|
|
121
|
+
db = AsyncMock()
|
|
122
|
+
db.users.get.return_value = {"id": 1, "name": "Test"}
|
|
123
|
+
return db
|
|
124
|
+
|
|
125
|
+
@pytest.mark.asyncio
|
|
126
|
+
async def test_get_user(mock_db):
|
|
127
|
+
result = await get_user(1, db=mock_db)
|
|
128
|
+
assert result["name"] == "Test"
|
|
129
|
+
mock_db.users.get.assert_called_once_with(1)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Core Principles
|
|
133
|
+
|
|
134
|
+
### Readability First
|
|
135
|
+
Python prioritizes readability. Code should be obvious and easy to understand.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
# Good: Clear and readable
|
|
139
|
+
def get_active_users(users: list[User]) -> list[User]:
|
|
140
|
+
"""Return only active users from the provided list."""
|
|
141
|
+
return [user for user in users if user.is_active]
|
|
142
|
+
|
|
143
|
+
# Bad: Clever but confusing
|
|
144
|
+
def get_active_users(u):
|
|
145
|
+
return [x for x in u if x.a]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### EAFP Over LBYL
|
|
149
|
+
Python prefers exception handling over checking conditions upfront.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Good: EAFP style (Easier to Ask Forgiveness than Permission)
|
|
153
|
+
def get_value(dictionary: dict, key: str, default=None):
|
|
154
|
+
try:
|
|
155
|
+
return dictionary[key]
|
|
156
|
+
except KeyError:
|
|
157
|
+
return default
|
|
158
|
+
|
|
159
|
+
# Bad: LBYL (Look Before You Leap) style
|
|
160
|
+
def get_value(dictionary: dict, key: str, default=None):
|
|
161
|
+
if key in dictionary:
|
|
162
|
+
return dictionary[key]
|
|
163
|
+
return default
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Error Handling Patterns
|
|
167
|
+
|
|
168
|
+
### Specific Exception Handling
|
|
169
|
+
```python
|
|
170
|
+
# Good: Catch specific exceptions with chaining
|
|
171
|
+
def load_config(path: str) -> Config:
|
|
172
|
+
try:
|
|
173
|
+
with open(path) as f:
|
|
174
|
+
return Config.from_json(f.read())
|
|
175
|
+
except FileNotFoundError as e:
|
|
176
|
+
raise ConfigError(f"Config file not found: {path}") from e
|
|
177
|
+
except json.JSONDecodeError as e:
|
|
178
|
+
raise ConfigError(f"Invalid JSON in config: {path}") from e
|
|
179
|
+
|
|
180
|
+
# Bad: Bare except with silent failure
|
|
181
|
+
def load_config(path: str) -> Config:
|
|
182
|
+
try:
|
|
183
|
+
with open(path) as f:
|
|
184
|
+
return Config.from_json(f.read())
|
|
185
|
+
except:
|
|
186
|
+
return None
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Custom Exception Hierarchy
|
|
190
|
+
```python
|
|
191
|
+
class AppError(Exception):
|
|
192
|
+
"""Base exception for all application errors."""
|
|
193
|
+
|
|
194
|
+
class ValidationError(AppError):
|
|
195
|
+
"""Raised when input validation fails."""
|
|
196
|
+
|
|
197
|
+
class NotFoundError(AppError):
|
|
198
|
+
"""Raised when a requested resource is not found."""
|
|
199
|
+
|
|
200
|
+
def get_user(user_id: str) -> User:
|
|
201
|
+
user = db.find_user(user_id)
|
|
202
|
+
if not user:
|
|
203
|
+
raise NotFoundError(f"User not found: {user_id}")
|
|
204
|
+
return user
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Context Managers
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from contextlib import contextmanager
|
|
211
|
+
|
|
212
|
+
# Custom context manager for timing
|
|
213
|
+
@contextmanager
|
|
214
|
+
def timer(name: str):
|
|
215
|
+
start = time.perf_counter()
|
|
216
|
+
yield
|
|
217
|
+
elapsed = time.perf_counter() - start
|
|
218
|
+
print(f"{name} took {elapsed:.4f} seconds")
|
|
219
|
+
|
|
220
|
+
# Class-based context manager for transactions
|
|
221
|
+
class DatabaseTransaction:
|
|
222
|
+
def __init__(self, connection):
|
|
223
|
+
self.connection = connection
|
|
224
|
+
|
|
225
|
+
def __enter__(self):
|
|
226
|
+
self.connection.begin_transaction()
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
230
|
+
if exc_type is None:
|
|
231
|
+
self.connection.commit()
|
|
232
|
+
else:
|
|
233
|
+
self.connection.rollback()
|
|
234
|
+
return False # Don't suppress exceptions
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Data Classes and Validation
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from dataclasses import dataclass, field
|
|
241
|
+
from datetime import datetime
|
|
242
|
+
|
|
243
|
+
@dataclass
|
|
244
|
+
class User:
|
|
245
|
+
id: str
|
|
246
|
+
name: str
|
|
247
|
+
email: str
|
|
248
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
249
|
+
is_active: bool = True
|
|
250
|
+
|
|
251
|
+
def __post_init__(self):
|
|
252
|
+
if "@" not in self.email:
|
|
253
|
+
raise ValueError(f"Invalid email: {self.email}")
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Decorators
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
import functools
|
|
260
|
+
|
|
261
|
+
# Function decorator with wraps
|
|
262
|
+
def timer(func):
|
|
263
|
+
@functools.wraps(func)
|
|
264
|
+
def wrapper(*args, **kwargs):
|
|
265
|
+
start = time.perf_counter()
|
|
266
|
+
result = func(*args, **kwargs)
|
|
267
|
+
elapsed = time.perf_counter() - start
|
|
268
|
+
print(f"{func.__name__} took {elapsed:.4f}s")
|
|
269
|
+
return result
|
|
270
|
+
return wrapper
|
|
271
|
+
|
|
272
|
+
# Parameterized decorator
|
|
273
|
+
def retry(max_attempts: int = 3, delay: float = 1.0):
|
|
274
|
+
def decorator(func):
|
|
275
|
+
@functools.wraps(func)
|
|
276
|
+
def wrapper(*args, **kwargs):
|
|
277
|
+
for attempt in range(max_attempts):
|
|
278
|
+
try:
|
|
279
|
+
return func(*args, **kwargs)
|
|
280
|
+
except Exception as e:
|
|
281
|
+
if attempt == max_attempts - 1:
|
|
282
|
+
raise
|
|
283
|
+
time.sleep(delay * (2 ** attempt))
|
|
284
|
+
return wrapper
|
|
285
|
+
return decorator
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Comprehensions and Generators
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
# List comprehensions for simple transforms
|
|
292
|
+
names = [user.name for user in users if user.is_active]
|
|
293
|
+
|
|
294
|
+
# Generator expressions for lazy evaluation (memory efficient)
|
|
295
|
+
total = sum(x * x for x in range(1_000_000))
|
|
296
|
+
|
|
297
|
+
# Generator functions for large data
|
|
298
|
+
def read_large_file(path: str) -> Iterator[str]:
|
|
299
|
+
with open(path) as f:
|
|
300
|
+
for line in f:
|
|
301
|
+
yield line.strip()
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Concurrency Patterns
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
import concurrent.futures
|
|
308
|
+
|
|
309
|
+
# Threading for I/O-bound tasks
|
|
310
|
+
def fetch_all_urls(urls: list[str]) -> dict[str, str]:
|
|
311
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
|
|
312
|
+
future_to_url = {executor.submit(fetch_url, url): url for url in urls}
|
|
313
|
+
results = {}
|
|
314
|
+
for future in concurrent.futures.as_completed(future_to_url):
|
|
315
|
+
url = future_to_url[future]
|
|
316
|
+
try:
|
|
317
|
+
results[url] = future.result()
|
|
318
|
+
except Exception as e:
|
|
319
|
+
results[url] = f"Error: {e}"
|
|
320
|
+
return results
|
|
321
|
+
|
|
322
|
+
# Multiprocessing for CPU-bound tasks
|
|
323
|
+
def process_all(datasets: list[list[int]]) -> list[int]:
|
|
324
|
+
with concurrent.futures.ProcessPoolExecutor() as executor:
|
|
325
|
+
return list(executor.map(process_data, datasets))
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Memory and Performance
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
# Use __slots__ for memory efficiency in many-instance classes
|
|
332
|
+
class Point:
|
|
333
|
+
__slots__ = ['x', 'y']
|
|
334
|
+
def __init__(self, x: float, y: float):
|
|
335
|
+
self.x = x
|
|
336
|
+
self.y = y
|
|
337
|
+
|
|
338
|
+
# Use join instead of concatenation in loops
|
|
339
|
+
result = "".join(str(item) for item in items) # O(n)
|
|
340
|
+
# NOT: result += str(item) # O(n²)
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Anti-Patterns to Avoid
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
# Bad: Mutable default arguments
|
|
347
|
+
def append_to(item, items=[]): # Shared across calls!
|
|
348
|
+
items.append(item)
|
|
349
|
+
return items
|
|
350
|
+
# Good: Use None sentinel
|
|
351
|
+
def append_to(item, items=None):
|
|
352
|
+
if items is None:
|
|
353
|
+
items = []
|
|
354
|
+
items.append(item)
|
|
355
|
+
return items
|
|
356
|
+
|
|
357
|
+
# Bad: type(obj) == list → Good: isinstance(obj, list)
|
|
358
|
+
# Bad: value == None → Good: value is None
|
|
359
|
+
# Bad: from module import * → Good: from module import specific_name
|
|
360
|
+
# Bad: bare except: → Good: except SpecificError as e:
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Python Tooling
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# Formatting and linting
|
|
367
|
+
ruff check . && ruff format .
|
|
368
|
+
mypy .
|
|
369
|
+
|
|
370
|
+
# Testing with coverage
|
|
371
|
+
pytest --cov=mypackage --cov-report=term-missing
|
|
372
|
+
|
|
373
|
+
# Security scanning
|
|
374
|
+
bandit -r .
|
|
375
|
+
pip-audit
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Best Practices
|
|
379
|
+
|
|
380
|
+
- Use `ruff` for linting and formatting
|
|
381
|
+
- Use `mypy` with strict mode
|
|
382
|
+
- Prefer `pathlib.Path` over `os.path`
|
|
383
|
+
- Use dataclasses or Pydantic for data structures
|
|
384
|
+
- Use `asyncio` for I/O-bound operations
|
|
385
|
+
- Use `contextlib.asynccontextmanager` for async resources
|
|
386
|
+
- Use Protocol classes for structural subtyping (duck typing)
|
|
387
|
+
- Prefer generators over lists for large datasets
|
|
388
|
+
- Follow import order: stdlib, third-party, local (use isort)
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-best-practices
|
|
3
|
+
description: Modern React development guidelines covering hooks, component patterns, state management, performance optimization, and TypeScript integration.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React Best Practices
|
|
7
|
+
|
|
8
|
+
Modern React development guidelines for building maintainable, performant applications.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- Building React applications (Next.js, Vite, CRA)
|
|
13
|
+
- Reviewing React code for quality
|
|
14
|
+
- Optimizing React app performance
|
|
15
|
+
- Learning modern React patterns (hooks, server components)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
### Step 1: Component Structure
|
|
22
|
+
|
|
23
|
+
#### Functional Components Only
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// Prefer: Arrow function with explicit return type
|
|
27
|
+
const UserCard: React.FC<UserCardProps> = ({ name, email, avatar }) => {
|
|
28
|
+
return (
|
|
29
|
+
<div className="user-card">
|
|
30
|
+
<img src={avatar} alt={`${name}'s avatar`} />
|
|
31
|
+
<h3>{name}</h3>
|
|
32
|
+
<p>{email}</p>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Props interface (always define)
|
|
38
|
+
interface UserCardProps {
|
|
39
|
+
name: string;
|
|
40
|
+
email: string;
|
|
41
|
+
avatar: string;
|
|
42
|
+
onSelect?: (email: string) => void;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### File Organization
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
src/
|
|
50
|
+
├── components/ # Reusable UI components
|
|
51
|
+
│ ├── Button/
|
|
52
|
+
│ │ ├── Button.tsx
|
|
53
|
+
│ │ ├── Button.test.tsx
|
|
54
|
+
│ │ └── index.ts
|
|
55
|
+
│ └── ...
|
|
56
|
+
├── hooks/ # Custom hooks
|
|
57
|
+
├── pages/ # Route-level components
|
|
58
|
+
├── services/ # API calls
|
|
59
|
+
├── types/ # Shared TypeScript types
|
|
60
|
+
└── utils/ # Utility functions
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Step 2: Hooks Patterns
|
|
64
|
+
|
|
65
|
+
#### useState -- Keep State Minimal
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// Good: derive values instead of storing them
|
|
69
|
+
const [items, setItems] = useState<Item[]>([]);
|
|
70
|
+
const totalPrice = items.reduce((sum, item) => sum + item.price, 0); // Derived
|
|
71
|
+
|
|
72
|
+
// Bad: redundant state
|
|
73
|
+
const [items, setItems] = useState<Item[]>([]);
|
|
74
|
+
const [totalPrice, setTotalPrice] = useState(0); // Don't do this
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### useEffect -- Cleanup and Dependencies
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
|
|
83
|
+
async function fetchData() {
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch(`/api/users/${userId}`, { signal: controller.signal });
|
|
86
|
+
const data = await res.json();
|
|
87
|
+
setUser(data);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (!controller.signal.aborted) setError(err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fetchData();
|
|
94
|
+
return () => controller.abort(); // Cleanup
|
|
95
|
+
}, [userId]); // Only re-run when userId changes
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Custom Hooks -- Extract Logic
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
function useDebounce<T>(value: T, delay: number): T {
|
|
102
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
106
|
+
return () => clearTimeout(timer);
|
|
107
|
+
}, [value, delay]);
|
|
108
|
+
|
|
109
|
+
return debouncedValue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Usage
|
|
113
|
+
const debouncedSearch = useDebounce(searchTerm, 300);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Step 3: State Management
|
|
117
|
+
|
|
118
|
+
#### Local State (useState) -- Default Choice
|
|
119
|
+
|
|
120
|
+
Use for: form inputs, toggle states, component-specific data.
|
|
121
|
+
|
|
122
|
+
#### Context -- Shared UI State
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
const ThemeContext = createContext<ThemeContextType | null>(null);
|
|
126
|
+
|
|
127
|
+
function useTheme() {
|
|
128
|
+
const context = useContext(ThemeContext);
|
|
129
|
+
if (!context) throw new Error("useTheme must be used within ThemeProvider");
|
|
130
|
+
return context;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Use for: theme, auth status, locale. **Not for** frequently changing data.
|
|
135
|
+
|
|
136
|
+
#### External Store (Zustand/Redux) -- Complex State
|
|
137
|
+
|
|
138
|
+
Use for: shopping cart, multi-step forms, real-time data.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
// Zustand example (lightweight)
|
|
142
|
+
const useStore = create<StoreState>((set) => ({
|
|
143
|
+
items: [],
|
|
144
|
+
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
|
|
145
|
+
removeItem: (id) => set((state) => ({ items: state.items.filter((i) => i.id !== id) })),
|
|
146
|
+
}));
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Step 4: Performance Optimization
|
|
150
|
+
|
|
151
|
+
#### Memoization (use sparingly)
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// React.memo -- prevent re-renders when props haven't changed
|
|
155
|
+
const ExpensiveList = React.memo(({ items }: { items: Item[] }) => {
|
|
156
|
+
return items.map((item) => <ListItem key={item.id} item={item} />);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// useMemo -- cache expensive computations
|
|
160
|
+
const sortedItems = useMemo(
|
|
161
|
+
() => items.sort((a, b) => a.name.localeCompare(b.name)),
|
|
162
|
+
[items]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// useCallback -- stable function references for child components
|
|
166
|
+
const handleClick = useCallback((id: string) => {
|
|
167
|
+
setSelected(id);
|
|
168
|
+
}, []);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### Lazy Loading
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
const Dashboard = React.lazy(() => import("./pages/Dashboard"));
|
|
175
|
+
|
|
176
|
+
function App() {
|
|
177
|
+
return (
|
|
178
|
+
<Suspense fallback={<LoadingSpinner />}>
|
|
179
|
+
<Dashboard />
|
|
180
|
+
</Suspense>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Virtualization for Large Lists
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
import { FixedSizeList } from "react-window";
|
|
189
|
+
|
|
190
|
+
<FixedSizeList height={400} itemCount={10000} itemSize={35} width="100%">
|
|
191
|
+
{({ index, style }) => <div style={style}>{items[index].name}</div>}
|
|
192
|
+
</FixedSizeList>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Step 5: Error Handling
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
class ErrorBoundary extends React.Component<Props, State> {
|
|
199
|
+
state = { hasError: false };
|
|
200
|
+
|
|
201
|
+
static getDerivedStateFromError() {
|
|
202
|
+
return { hasError: true };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
206
|
+
console.error("React error:", error, info);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
render() {
|
|
210
|
+
if (this.state.hasError) {
|
|
211
|
+
return <div>Something went wrong. Please refresh.</div>;
|
|
212
|
+
}
|
|
213
|
+
return this.props.children;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Patterns and Examples
|
|
221
|
+
|
|
222
|
+
### Data Fetching Pattern (with loading/error states)
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
function useQuery<T>(url: string) {
|
|
226
|
+
const [data, setData] = useState<T | null>(null);
|
|
227
|
+
const [loading, setLoading] = useState(true);
|
|
228
|
+
const [error, setError] = useState<Error | null>(null);
|
|
229
|
+
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
const controller = new AbortController();
|
|
232
|
+
setLoading(true);
|
|
233
|
+
fetch(url, { signal: controller.signal })
|
|
234
|
+
.then((res) => res.json())
|
|
235
|
+
.then(setData)
|
|
236
|
+
.catch((err) => !controller.signal.aborted && setError(err))
|
|
237
|
+
.finally(() => setLoading(false));
|
|
238
|
+
return () => controller.abort();
|
|
239
|
+
}, [url]);
|
|
240
|
+
|
|
241
|
+
return { data, loading, error };
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Conditional Rendering
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
// Early return for loading/error
|
|
249
|
+
if (loading) return <Skeleton />;
|
|
250
|
+
if (error) return <ErrorMessage error={error} />;
|
|
251
|
+
if (!data?.length) return <EmptyState message="No items found" />;
|
|
252
|
+
|
|
253
|
+
return <ItemList items={data} />;
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Checklist
|
|
259
|
+
|
|
260
|
+
- [ ] All components are functional (no class components unless ErrorBoundary)
|
|
261
|
+
- [ ] Props have TypeScript interfaces
|
|
262
|
+
- [ ] Custom hooks extract reusable logic
|
|
263
|
+
- [ ] useEffect has proper cleanup and dependencies
|
|
264
|
+
- [ ] State is minimal (derive values, don't duplicate)
|
|
265
|
+
- [ ] Memoization used only where measured benefit exists
|
|
266
|
+
- [ ] Large lists use virtualization
|
|
267
|
+
- [ ] Error boundaries wrap key sections
|
|
268
|
+
- [ ] Loading and empty states handled
|
|
269
|
+
- [ ] Keys are stable (not array index for dynamic lists)
|