omgkit 2.1.0 → 2.2.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/package.json +1 -1
- package/plugin/skills/SKILL_STANDARDS.md +743 -0
- package/plugin/skills/databases/mongodb/SKILL.md +797 -28
- package/plugin/skills/databases/postgresql/SKILL.md +494 -18
- package/plugin/skills/databases/prisma/SKILL.md +776 -30
- package/plugin/skills/databases/redis/SKILL.md +885 -25
- package/plugin/skills/devops/aws/SKILL.md +686 -28
- package/plugin/skills/devops/docker/SKILL.md +466 -18
- package/plugin/skills/devops/github-actions/SKILL.md +684 -29
- package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
- package/plugin/skills/frameworks/django/SKILL.md +920 -20
- package/plugin/skills/frameworks/express/SKILL.md +1361 -35
- package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
- package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
- package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
- package/plugin/skills/frameworks/nextjs/SKILL.md +407 -44
- package/plugin/skills/frameworks/rails/SKILL.md +594 -28
- package/plugin/skills/frameworks/react/SKILL.md +1006 -32
- package/plugin/skills/frameworks/spring/SKILL.md +528 -35
- package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
- package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
- package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
- package/plugin/skills/frontend/responsive/SKILL.md +847 -21
- package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
- package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
- package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
- package/plugin/skills/languages/javascript/SKILL.md +935 -31
- package/plugin/skills/languages/python/SKILL.md +489 -25
- package/plugin/skills/languages/typescript/SKILL.md +379 -30
- package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
- package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
- package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
- package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
- package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
- package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
- package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
- package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
- package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
- package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
- package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
- package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
- package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
- package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
- package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
- package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
- package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
- package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
- package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
- package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
- package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
- package/plugin/skills/security/better-auth/SKILL.md +1065 -28
- package/plugin/skills/security/oauth/SKILL.md +968 -31
- package/plugin/skills/security/owasp/SKILL.md +894 -33
- package/plugin/skills/testing/playwright/SKILL.md +764 -38
- package/plugin/skills/testing/pytest/SKILL.md +873 -36
- package/plugin/skills/testing/vitest/SKILL.md +980 -35
|
@@ -1,63 +1,527 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: python
|
|
3
|
-
description: Python development
|
|
3
|
+
description: Python development with type hints, async patterns, testing, and modern best practices
|
|
4
|
+
category: languages
|
|
5
|
+
triggers:
|
|
6
|
+
- python
|
|
7
|
+
- py
|
|
8
|
+
- pip
|
|
9
|
+
- pydantic
|
|
10
|
+
- fastapi
|
|
11
|
+
- django
|
|
4
12
|
---
|
|
5
13
|
|
|
6
|
-
# Python
|
|
14
|
+
# Python
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
Modern **Python development** following industry best practices. This skill covers type hints, async programming, data validation, testing, and production-ready patterns used by top engineering teams.
|
|
17
|
+
|
|
18
|
+
## Purpose
|
|
19
|
+
|
|
20
|
+
Write clean, maintainable Python code:
|
|
21
|
+
|
|
22
|
+
- Use type hints for better code quality
|
|
23
|
+
- Implement async patterns for I/O operations
|
|
24
|
+
- Validate data with Pydantic
|
|
25
|
+
- Structure projects properly
|
|
26
|
+
- Write comprehensive tests
|
|
27
|
+
- Follow PEP standards
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
### 1. Type Hints and Annotations
|
|
9
32
|
|
|
10
|
-
### Type Hints
|
|
11
33
|
```python
|
|
34
|
+
from typing import Optional, List, Dict, Union, Callable, TypeVar, Generic
|
|
35
|
+
from dataclasses import dataclass
|
|
36
|
+
from datetime import datetime
|
|
37
|
+
|
|
38
|
+
# Basic type hints
|
|
39
|
+
def greet(name: str) -> str:
|
|
40
|
+
return f"Hello, {name}"
|
|
41
|
+
|
|
42
|
+
def process_items(items: List[str], limit: int = 10) -> Dict[str, int]:
|
|
43
|
+
return {item: len(item) for item in items[:limit]}
|
|
44
|
+
|
|
45
|
+
# Optional and Union
|
|
46
|
+
def find_user(user_id: str) -> Optional[User]:
|
|
47
|
+
return db.users.get(user_id)
|
|
48
|
+
|
|
49
|
+
def parse_input(value: Union[str, int]) -> str:
|
|
50
|
+
return str(value)
|
|
51
|
+
|
|
52
|
+
# Callable types
|
|
53
|
+
Handler = Callable[[str, int], bool]
|
|
54
|
+
|
|
55
|
+
def register_handler(name: str, handler: Handler) -> None:
|
|
56
|
+
handlers[name] = handler
|
|
57
|
+
|
|
58
|
+
# Generic types
|
|
59
|
+
T = TypeVar('T')
|
|
60
|
+
|
|
61
|
+
class Repository(Generic[T]):
|
|
62
|
+
def __init__(self, model: type[T]) -> None:
|
|
63
|
+
self.model = model
|
|
64
|
+
|
|
65
|
+
def find_by_id(self, id: str) -> Optional[T]:
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
def find_all(self) -> List[T]:
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
def create(self, data: Dict) -> T:
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
# Type aliases
|
|
75
|
+
UserId = str
|
|
76
|
+
UserMap = Dict[UserId, User]
|
|
77
|
+
EventHandler = Callable[[Event], None]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. Dataclasses and Pydantic
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from dataclasses import dataclass, field
|
|
12
84
|
from typing import Optional, List
|
|
85
|
+
from datetime import datetime
|
|
86
|
+
from pydantic import BaseModel, EmailStr, Field, validator
|
|
87
|
+
from enum import Enum
|
|
88
|
+
|
|
89
|
+
# Dataclasses for simple data structures
|
|
90
|
+
@dataclass
|
|
91
|
+
class User:
|
|
92
|
+
id: str
|
|
93
|
+
email: str
|
|
94
|
+
name: str
|
|
95
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
96
|
+
roles: List[str] = field(default_factory=list)
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True)
|
|
99
|
+
class Point:
|
|
100
|
+
x: float
|
|
101
|
+
y: float
|
|
13
102
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
...
|
|
103
|
+
def distance_to(self, other: 'Point') -> float:
|
|
104
|
+
return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
|
|
17
105
|
|
|
18
|
-
|
|
19
|
-
|
|
106
|
+
# Pydantic for validation
|
|
107
|
+
class UserRole(str, Enum):
|
|
108
|
+
ADMIN = "admin"
|
|
109
|
+
USER = "user"
|
|
110
|
+
GUEST = "guest"
|
|
111
|
+
|
|
112
|
+
class UserCreate(BaseModel):
|
|
113
|
+
email: EmailStr
|
|
114
|
+
password: str = Field(..., min_length=8)
|
|
115
|
+
name: str = Field(..., min_length=2, max_length=100)
|
|
116
|
+
role: UserRole = UserRole.USER
|
|
117
|
+
|
|
118
|
+
@validator('password')
|
|
119
|
+
def password_strength(cls, v: str) -> str:
|
|
120
|
+
if not any(c.isupper() for c in v):
|
|
121
|
+
raise ValueError('Password must contain uppercase letter')
|
|
122
|
+
if not any(c.isdigit() for c in v):
|
|
123
|
+
raise ValueError('Password must contain digit')
|
|
124
|
+
return v
|
|
125
|
+
|
|
126
|
+
class Config:
|
|
127
|
+
str_strip_whitespace = True
|
|
128
|
+
|
|
129
|
+
class UserResponse(BaseModel):
|
|
130
|
+
id: str
|
|
131
|
+
email: str
|
|
132
|
+
name: str
|
|
133
|
+
role: UserRole
|
|
134
|
+
created_at: datetime
|
|
135
|
+
|
|
136
|
+
class Config:
|
|
137
|
+
from_attributes = True # For ORM compatibility
|
|
138
|
+
|
|
139
|
+
class PaginatedResponse(BaseModel, Generic[T]):
|
|
140
|
+
data: List[T]
|
|
141
|
+
total: int
|
|
142
|
+
page: int
|
|
143
|
+
limit: int
|
|
144
|
+
has_more: bool
|
|
20
145
|
```
|
|
21
146
|
|
|
22
|
-
### Async
|
|
147
|
+
### 3. Async Programming
|
|
148
|
+
|
|
23
149
|
```python
|
|
24
150
|
import asyncio
|
|
151
|
+
from typing import List, Dict, Any
|
|
25
152
|
import aiohttp
|
|
153
|
+
import asyncpg
|
|
26
154
|
|
|
27
|
-
|
|
155
|
+
# Basic async functions
|
|
156
|
+
async def fetch_data(url: str) -> Dict[str, Any]:
|
|
28
157
|
async with aiohttp.ClientSession() as session:
|
|
29
158
|
async with session.get(url) as response:
|
|
30
159
|
return await response.json()
|
|
160
|
+
|
|
161
|
+
# Concurrent requests
|
|
162
|
+
async def fetch_all(urls: List[str]) -> List[Dict[str, Any]]:
|
|
163
|
+
async with aiohttp.ClientSession() as session:
|
|
164
|
+
tasks = [fetch_url(session, url) for url in urls]
|
|
165
|
+
return await asyncio.gather(*tasks)
|
|
166
|
+
|
|
167
|
+
async def fetch_url(session: aiohttp.ClientSession, url: str) -> Dict[str, Any]:
|
|
168
|
+
async with session.get(url) as response:
|
|
169
|
+
return await response.json()
|
|
170
|
+
|
|
171
|
+
# Database operations
|
|
172
|
+
class Database:
|
|
173
|
+
def __init__(self, dsn: str):
|
|
174
|
+
self.dsn = dsn
|
|
175
|
+
self.pool: Optional[asyncpg.Pool] = None
|
|
176
|
+
|
|
177
|
+
async def connect(self) -> None:
|
|
178
|
+
self.pool = await asyncpg.create_pool(
|
|
179
|
+
self.dsn,
|
|
180
|
+
min_size=5,
|
|
181
|
+
max_size=20,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
async def disconnect(self) -> None:
|
|
185
|
+
if self.pool:
|
|
186
|
+
await self.pool.close()
|
|
187
|
+
|
|
188
|
+
async def fetch_user(self, user_id: str) -> Optional[Dict]:
|
|
189
|
+
async with self.pool.acquire() as conn:
|
|
190
|
+
row = await conn.fetchrow(
|
|
191
|
+
"SELECT * FROM users WHERE id = $1",
|
|
192
|
+
user_id
|
|
193
|
+
)
|
|
194
|
+
return dict(row) if row else None
|
|
195
|
+
|
|
196
|
+
async def create_user(self, email: str, name: str) -> Dict:
|
|
197
|
+
async with self.pool.acquire() as conn:
|
|
198
|
+
row = await conn.fetchrow(
|
|
199
|
+
"""
|
|
200
|
+
INSERT INTO users (email, name)
|
|
201
|
+
VALUES ($1, $2)
|
|
202
|
+
RETURNING *
|
|
203
|
+
""",
|
|
204
|
+
email, name
|
|
205
|
+
)
|
|
206
|
+
return dict(row)
|
|
207
|
+
|
|
208
|
+
# Context managers
|
|
209
|
+
class AsyncResource:
|
|
210
|
+
async def __aenter__(self) -> 'AsyncResource':
|
|
211
|
+
await self.setup()
|
|
212
|
+
return self
|
|
213
|
+
|
|
214
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
215
|
+
await self.cleanup()
|
|
216
|
+
|
|
217
|
+
# Rate limiting
|
|
218
|
+
class RateLimiter:
|
|
219
|
+
def __init__(self, rate: int, per: float):
|
|
220
|
+
self.rate = rate
|
|
221
|
+
self.per = per
|
|
222
|
+
self.semaphore = asyncio.Semaphore(rate)
|
|
223
|
+
|
|
224
|
+
async def acquire(self) -> None:
|
|
225
|
+
await self.semaphore.acquire()
|
|
226
|
+
asyncio.create_task(self._release())
|
|
227
|
+
|
|
228
|
+
async def _release(self) -> None:
|
|
229
|
+
await asyncio.sleep(self.per)
|
|
230
|
+
self.semaphore.release()
|
|
31
231
|
```
|
|
32
232
|
|
|
33
|
-
###
|
|
233
|
+
### 4. Error Handling
|
|
234
|
+
|
|
34
235
|
```python
|
|
236
|
+
from typing import TypeVar, Generic
|
|
35
237
|
from dataclasses import dataclass
|
|
36
|
-
|
|
238
|
+
|
|
239
|
+
# Custom exceptions
|
|
240
|
+
class AppError(Exception):
|
|
241
|
+
def __init__(self, message: str, code: str, status_code: int = 500):
|
|
242
|
+
self.message = message
|
|
243
|
+
self.code = code
|
|
244
|
+
self.status_code = status_code
|
|
245
|
+
super().__init__(message)
|
|
246
|
+
|
|
247
|
+
class NotFoundError(AppError):
|
|
248
|
+
def __init__(self, resource: str, id: str):
|
|
249
|
+
super().__init__(
|
|
250
|
+
f"{resource} with id {id} not found",
|
|
251
|
+
"NOT_FOUND",
|
|
252
|
+
404
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
class ValidationError(AppError):
|
|
256
|
+
def __init__(self, field: str, message: str):
|
|
257
|
+
super().__init__(
|
|
258
|
+
f"Validation error on {field}: {message}",
|
|
259
|
+
"VALIDATION_ERROR",
|
|
260
|
+
400
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Result type pattern
|
|
264
|
+
T = TypeVar('T')
|
|
265
|
+
E = TypeVar('E', bound=Exception)
|
|
37
266
|
|
|
38
267
|
@dataclass
|
|
39
|
-
class
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
268
|
+
class Ok(Generic[T]):
|
|
269
|
+
value: T
|
|
270
|
+
|
|
271
|
+
def is_ok(self) -> bool:
|
|
272
|
+
return True
|
|
273
|
+
|
|
274
|
+
def is_err(self) -> bool:
|
|
275
|
+
return False
|
|
276
|
+
|
|
277
|
+
@dataclass
|
|
278
|
+
class Err(Generic[E]):
|
|
279
|
+
error: E
|
|
280
|
+
|
|
281
|
+
def is_ok(self) -> bool:
|
|
282
|
+
return False
|
|
283
|
+
|
|
284
|
+
def is_err(self) -> bool:
|
|
285
|
+
return True
|
|
286
|
+
|
|
287
|
+
Result = Ok[T] | Err[E]
|
|
288
|
+
|
|
289
|
+
def divide(a: float, b: float) -> Result[float, ValueError]:
|
|
290
|
+
if b == 0:
|
|
291
|
+
return Err(ValueError("Division by zero"))
|
|
292
|
+
return Ok(a / b)
|
|
293
|
+
|
|
294
|
+
# Usage
|
|
295
|
+
result = divide(10, 2)
|
|
296
|
+
if result.is_ok():
|
|
297
|
+
print(f"Result: {result.value}")
|
|
298
|
+
else:
|
|
299
|
+
print(f"Error: {result.error}")
|
|
43
300
|
```
|
|
44
301
|
|
|
45
|
-
###
|
|
302
|
+
### 5. Testing with Pytest
|
|
303
|
+
|
|
46
304
|
```python
|
|
47
|
-
|
|
305
|
+
import pytest
|
|
306
|
+
from unittest.mock import Mock, patch, AsyncMock
|
|
307
|
+
from datetime import datetime
|
|
48
308
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
309
|
+
# Basic tests
|
|
310
|
+
def test_create_user():
|
|
311
|
+
user = create_user("test@example.com", "Test User")
|
|
312
|
+
assert user.email == "test@example.com"
|
|
313
|
+
assert user.name == "Test User"
|
|
314
|
+
|
|
315
|
+
# Parametrized tests
|
|
316
|
+
@pytest.mark.parametrize("email,expected", [
|
|
317
|
+
("valid@example.com", True),
|
|
318
|
+
("invalid-email", False),
|
|
319
|
+
("", False),
|
|
320
|
+
])
|
|
321
|
+
def test_validate_email(email: str, expected: bool):
|
|
322
|
+
assert validate_email(email) == expected
|
|
323
|
+
|
|
324
|
+
# Fixtures
|
|
325
|
+
@pytest.fixture
|
|
326
|
+
def user():
|
|
327
|
+
return User(
|
|
328
|
+
id="test-id",
|
|
329
|
+
email="test@example.com",
|
|
330
|
+
name="Test User"
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
@pytest.fixture
|
|
334
|
+
def db():
|
|
335
|
+
db = Database(":memory:")
|
|
336
|
+
db.connect()
|
|
337
|
+
yield db
|
|
338
|
+
db.disconnect()
|
|
339
|
+
|
|
340
|
+
def test_user_creation(user: User):
|
|
341
|
+
assert user.id == "test-id"
|
|
342
|
+
|
|
343
|
+
# Async tests
|
|
344
|
+
@pytest.mark.asyncio
|
|
345
|
+
async def test_fetch_user():
|
|
346
|
+
db = AsyncMock()
|
|
347
|
+
db.fetch_user.return_value = {"id": "1", "name": "Test"}
|
|
348
|
+
|
|
349
|
+
result = await db.fetch_user("1")
|
|
350
|
+
assert result["name"] == "Test"
|
|
351
|
+
|
|
352
|
+
# Mocking
|
|
353
|
+
def test_external_api_call():
|
|
354
|
+
with patch('module.requests.get') as mock_get:
|
|
355
|
+
mock_get.return_value.json.return_value = {"data": "value"}
|
|
356
|
+
|
|
357
|
+
result = fetch_external_data()
|
|
358
|
+
|
|
359
|
+
assert result == {"data": "value"}
|
|
360
|
+
mock_get.assert_called_once()
|
|
361
|
+
|
|
362
|
+
# Exception testing
|
|
363
|
+
def test_not_found_raises():
|
|
364
|
+
with pytest.raises(NotFoundError) as exc_info:
|
|
365
|
+
find_user("nonexistent")
|
|
366
|
+
|
|
367
|
+
assert "not found" in str(exc_info.value)
|
|
368
|
+
|
|
369
|
+
# Test class organization
|
|
370
|
+
class TestUserService:
|
|
371
|
+
@pytest.fixture(autouse=True)
|
|
372
|
+
def setup(self):
|
|
373
|
+
self.service = UserService()
|
|
374
|
+
self.mock_repo = Mock()
|
|
375
|
+
self.service.repo = self.mock_repo
|
|
376
|
+
|
|
377
|
+
def test_create_user_success(self):
|
|
378
|
+
self.mock_repo.create.return_value = User(id="1", email="test@example.com")
|
|
379
|
+
|
|
380
|
+
user = self.service.create_user("test@example.com", "password")
|
|
381
|
+
|
|
382
|
+
assert user.email == "test@example.com"
|
|
383
|
+
|
|
384
|
+
def test_create_user_duplicate_email(self):
|
|
385
|
+
self.mock_repo.find_by_email.return_value = User(id="1", email="test@example.com")
|
|
386
|
+
|
|
387
|
+
with pytest.raises(ValidationError):
|
|
388
|
+
self.service.create_user("test@example.com", "password")
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### 6. Project Structure
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
project/
|
|
395
|
+
├── src/
|
|
396
|
+
│ └── myapp/
|
|
397
|
+
│ ├── __init__.py
|
|
398
|
+
│ ├── main.py
|
|
399
|
+
│ ├── config.py
|
|
400
|
+
│ ├── models/
|
|
401
|
+
│ │ ├── __init__.py
|
|
402
|
+
│ │ └── user.py
|
|
403
|
+
│ ├── services/
|
|
404
|
+
│ │ ├── __init__.py
|
|
405
|
+
│ │ └── user_service.py
|
|
406
|
+
│ ├── repositories/
|
|
407
|
+
│ │ ├── __init__.py
|
|
408
|
+
│ │ └── user_repository.py
|
|
409
|
+
│ └── api/
|
|
410
|
+
│ ├── __init__.py
|
|
411
|
+
│ ├── routes.py
|
|
412
|
+
│ └── dependencies.py
|
|
413
|
+
├── tests/
|
|
414
|
+
│ ├── __init__.py
|
|
415
|
+
│ ├── conftest.py
|
|
416
|
+
│ ├── unit/
|
|
417
|
+
│ │ └── test_user_service.py
|
|
418
|
+
│ └── integration/
|
|
419
|
+
│ └── test_api.py
|
|
420
|
+
├── pyproject.toml
|
|
421
|
+
├── requirements.txt
|
|
422
|
+
└── README.md
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### 7. Configuration Management
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
from pydantic_settings import BaseSettings
|
|
429
|
+
from functools import lru_cache
|
|
430
|
+
from typing import Optional
|
|
431
|
+
|
|
432
|
+
class Settings(BaseSettings):
|
|
433
|
+
app_name: str = "MyApp"
|
|
434
|
+
debug: bool = False
|
|
435
|
+
|
|
436
|
+
database_url: str
|
|
437
|
+
redis_url: Optional[str] = None
|
|
438
|
+
|
|
439
|
+
secret_key: str
|
|
440
|
+
jwt_algorithm: str = "HS256"
|
|
441
|
+
jwt_expire_minutes: int = 30
|
|
442
|
+
|
|
443
|
+
cors_origins: list[str] = ["http://localhost:3000"]
|
|
52
444
|
|
|
53
445
|
class Config:
|
|
54
|
-
|
|
446
|
+
env_file = ".env"
|
|
447
|
+
env_file_encoding = "utf-8"
|
|
448
|
+
|
|
449
|
+
@lru_cache
|
|
450
|
+
def get_settings() -> Settings:
|
|
451
|
+
return Settings()
|
|
452
|
+
|
|
453
|
+
# Usage
|
|
454
|
+
settings = get_settings()
|
|
455
|
+
print(settings.database_url)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Use Cases
|
|
459
|
+
|
|
460
|
+
### FastAPI Application
|
|
461
|
+
```python
|
|
462
|
+
from fastapi import FastAPI, Depends, HTTPException
|
|
463
|
+
from sqlalchemy.orm import Session
|
|
464
|
+
|
|
465
|
+
app = FastAPI()
|
|
466
|
+
|
|
467
|
+
@app.get("/users/{user_id}", response_model=UserResponse)
|
|
468
|
+
async def get_user(user_id: str, db: Session = Depends(get_db)):
|
|
469
|
+
user = await db.users.find_by_id(user_id)
|
|
470
|
+
if not user:
|
|
471
|
+
raise HTTPException(status_code=404, detail="User not found")
|
|
472
|
+
return user
|
|
473
|
+
|
|
474
|
+
@app.post("/users", response_model=UserResponse, status_code=201)
|
|
475
|
+
async def create_user(data: UserCreate, db: Session = Depends(get_db)):
|
|
476
|
+
existing = await db.users.find_by_email(data.email)
|
|
477
|
+
if existing:
|
|
478
|
+
raise HTTPException(status_code=400, detail="Email already exists")
|
|
479
|
+
return await db.users.create(data.dict())
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### CLI Application
|
|
483
|
+
```python
|
|
484
|
+
import click
|
|
485
|
+
|
|
486
|
+
@click.group()
|
|
487
|
+
def cli():
|
|
488
|
+
"""My CLI application."""
|
|
489
|
+
pass
|
|
490
|
+
|
|
491
|
+
@cli.command()
|
|
492
|
+
@click.option('--name', prompt='Your name', help='User name')
|
|
493
|
+
@click.option('--count', default=1, help='Number of greetings')
|
|
494
|
+
def hello(name: str, count: int):
|
|
495
|
+
"""Greet the user."""
|
|
496
|
+
for _ in range(count):
|
|
497
|
+
click.echo(f'Hello, {name}!')
|
|
498
|
+
|
|
499
|
+
if __name__ == '__main__':
|
|
500
|
+
cli()
|
|
55
501
|
```
|
|
56
502
|
|
|
57
503
|
## Best Practices
|
|
504
|
+
|
|
505
|
+
### Do's
|
|
58
506
|
- Use type hints everywhere
|
|
59
507
|
- Use dataclasses or Pydantic for data
|
|
60
508
|
- Use async for I/O operations
|
|
61
|
-
- Follow PEP 8
|
|
509
|
+
- Follow PEP 8 and PEP 257
|
|
62
510
|
- Use virtual environments
|
|
63
|
-
- Write docstrings
|
|
511
|
+
- Write comprehensive docstrings
|
|
512
|
+
- Use pytest for testing
|
|
513
|
+
|
|
514
|
+
### Don'ts
|
|
515
|
+
- Don't use mutable default arguments
|
|
516
|
+
- Don't catch bare exceptions
|
|
517
|
+
- Don't ignore type errors
|
|
518
|
+
- Don't use global state
|
|
519
|
+
- Don't skip error handling
|
|
520
|
+
- Don't write untestable code
|
|
521
|
+
|
|
522
|
+
## References
|
|
523
|
+
|
|
524
|
+
- [Python Documentation](https://docs.python.org/3/)
|
|
525
|
+
- [PEP 8 Style Guide](https://peps.python.org/pep-0008/)
|
|
526
|
+
- [Pydantic Documentation](https://docs.pydantic.dev/)
|
|
527
|
+
- [Pytest Documentation](https://docs.pytest.org/)
|