home / skills / vudovn / antigravity-kit / python-patterns
This skill helps you think through Python framework choices, async patterns, type hints, and project structure to make informed, context-driven decisions.
npx playbooks add skill vudovn/antigravity-kit --skill python-patternsReview the files below or copy the command above to add this skill to your agents.
---
name: python-patterns
description: Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Teaches thinking, not copying.
allowed-tools: Read, Write, Edit, Glob, Grep
---
# Python Patterns
> Python development principles and decision-making for 2025.
> **Learn to THINK, not memorize patterns.**
---
## ⚠️ How to Use This Skill
This skill teaches **decision-making principles**, not fixed code to copy.
- ASK user for framework preference when unclear
- Choose async vs sync based on CONTEXT
- Don't default to same framework every time
---
## 1. Framework Selection (2025)
### Decision Tree
```
What are you building?
│
├── API-first / Microservices
│ └── FastAPI (async, modern, fast)
│
├── Full-stack web / CMS / Admin
│ └── Django (batteries-included)
│
├── Simple / Script / Learning
│ └── Flask (minimal, flexible)
│
├── AI/ML API serving
│ └── FastAPI (Pydantic, async, uvicorn)
│
└── Background workers
└── Celery + any framework
```
### Comparison Principles
| Factor | FastAPI | Django | Flask |
|--------|---------|--------|-------|
| **Best for** | APIs, microservices | Full-stack, CMS | Simple, learning |
| **Async** | Native | Django 5.0+ | Via extensions |
| **Admin** | Manual | Built-in | Via extensions |
| **ORM** | Choose your own | Django ORM | Choose your own |
| **Learning curve** | Low | Medium | Low |
### Selection Questions to Ask:
1. Is this API-only or full-stack?
2. Need admin interface?
3. Team familiar with async?
4. Existing infrastructure?
---
## 2. Async vs Sync Decision
### When to Use Async
```
async def is better when:
├── I/O-bound operations (database, HTTP, file)
├── Many concurrent connections
├── Real-time features
├── Microservices communication
└── FastAPI/Starlette/Django ASGI
def (sync) is better when:
├── CPU-bound operations
├── Simple scripts
├── Legacy codebase
├── Team unfamiliar with async
└── Blocking libraries (no async version)
```
### The Golden Rule
```
I/O-bound → async (waiting for external)
CPU-bound → sync + multiprocessing (computing)
Don't:
├── Mix sync and async carelessly
├── Use sync libraries in async code
└── Force async for CPU work
```
### Async Library Selection
| Need | Async Library |
|------|---------------|
| HTTP client | httpx |
| PostgreSQL | asyncpg |
| Redis | aioredis / redis-py async |
| File I/O | aiofiles |
| Database ORM | SQLAlchemy 2.0 async, Tortoise |
---
## 3. Type Hints Strategy
### When to Type
```
Always type:
├── Function parameters
├── Return types
├── Class attributes
├── Public APIs
Can skip:
├── Local variables (let inference work)
├── One-off scripts
├── Tests (usually)
```
### Common Type Patterns
```python
# These are patterns, understand them:
# Optional → might be None
from typing import Optional
def find_user(id: int) -> Optional[User]: ...
# Union → one of multiple types
def process(data: str | dict) -> None: ...
# Generic collections
def get_items() -> list[Item]: ...
def get_mapping() -> dict[str, int]: ...
# Callable
from typing import Callable
def apply(fn: Callable[[int], str]) -> str: ...
```
### Pydantic for Validation
```
When to use Pydantic:
├── API request/response models
├── Configuration/settings
├── Data validation
├── Serialization
Benefits:
├── Runtime validation
├── Auto-generated JSON schema
├── Works with FastAPI natively
└── Clear error messages
```
---
## 4. Project Structure Principles
### Structure Selection
```
Small project / Script:
├── main.py
├── utils.py
└── requirements.txt
Medium API:
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── models/
│ ├── routes/
│ ├── services/
│ └── schemas/
├── tests/
└── pyproject.toml
Large application:
├── src/
│ └── myapp/
│ ├── core/
│ ├── api/
│ ├── services/
│ ├── models/
│ └── ...
├── tests/
└── pyproject.toml
```
### FastAPI Structure Principles
```
Organize by feature or layer:
By layer:
├── routes/ (API endpoints)
├── services/ (business logic)
├── models/ (database models)
├── schemas/ (Pydantic models)
└── dependencies/ (shared deps)
By feature:
├── users/
│ ├── routes.py
│ ├── service.py
│ └── schemas.py
└── products/
└── ...
```
---
## 5. Django Principles (2025)
### Django Async (Django 5.0+)
```
Django supports async:
├── Async views
├── Async middleware
├── Async ORM (limited)
└── ASGI deployment
When to use async in Django:
├── External API calls
├── WebSocket (Channels)
├── High-concurrency views
└── Background task triggering
```
### Django Best Practices
```
Model design:
├── Fat models, thin views
├── Use managers for common queries
├── Abstract base classes for shared fields
Views:
├── Class-based for complex CRUD
├── Function-based for simple endpoints
├── Use viewsets with DRF
Queries:
├── select_related() for FKs
├── prefetch_related() for M2M
├── Avoid N+1 queries
└── Use .only() for specific fields
```
---
## 6. FastAPI Principles
### async def vs def in FastAPI
```
Use async def when:
├── Using async database drivers
├── Making async HTTP calls
├── I/O-bound operations
└── Want to handle concurrency
Use def when:
├── Blocking operations
├── Sync database drivers
├── CPU-bound work
└── FastAPI runs in threadpool automatically
```
### Dependency Injection
```
Use dependencies for:
├── Database sessions
├── Current user / Auth
├── Configuration
├── Shared resources
Benefits:
├── Testability (mock dependencies)
├── Clean separation
├── Automatic cleanup (yield)
```
### Pydantic v2 Integration
```python
# FastAPI + Pydantic are tightly integrated:
# Request validation
@app.post("/users")
async def create(user: UserCreate) -> UserResponse:
# user is already validated
...
# Response serialization
# Return type becomes response schema
```
---
## 7. Background Tasks
### Selection Guide
| Solution | Best For |
|----------|----------|
| **BackgroundTasks** | Simple, in-process tasks |
| **Celery** | Distributed, complex workflows |
| **ARQ** | Async, Redis-based |
| **RQ** | Simple Redis queue |
| **Dramatiq** | Actor-based, simpler than Celery |
### When to Use Each
```
FastAPI BackgroundTasks:
├── Quick operations
├── No persistence needed
├── Fire-and-forget
└── Same process
Celery/ARQ:
├── Long-running tasks
├── Need retry logic
├── Distributed workers
├── Persistent queue
└── Complex workflows
```
---
## 8. Error Handling Principles
### Exception Strategy
```
In FastAPI:
├── Create custom exception classes
├── Register exception handlers
├── Return consistent error format
└── Log without exposing internals
Pattern:
├── Raise domain exceptions in services
├── Catch and transform in handlers
└── Client gets clean error response
```
### Error Response Philosophy
```
Include:
├── Error code (programmatic)
├── Message (human readable)
├── Details (field-level when applicable)
└── NOT stack traces (security)
```
---
## 9. Testing Principles
### Testing Strategy
| Type | Purpose | Tools |
|------|---------|-------|
| **Unit** | Business logic | pytest |
| **Integration** | API endpoints | pytest + httpx/TestClient |
| **E2E** | Full workflows | pytest + DB |
### Async Testing
```python
# Use pytest-asyncio for async tests
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_endpoint():
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.get("/users")
assert response.status_code == 200
```
### Fixtures Strategy
```
Common fixtures:
├── db_session → Database connection
├── client → Test client
├── authenticated_user → User with token
└── sample_data → Test data setup
```
---
## 10. Decision Checklist
Before implementing:
- [ ] **Asked user about framework preference?**
- [ ] **Chosen framework for THIS context?** (not just default)
- [ ] **Decided async vs sync?**
- [ ] **Planned type hint strategy?**
- [ ] **Defined project structure?**
- [ ] **Planned error handling?**
- [ ] **Considered background tasks?**
---
## 11. Anti-Patterns to Avoid
### ❌ DON'T:
- Default to Django for simple APIs (FastAPI may be better)
- Use sync libraries in async code
- Skip type hints for public APIs
- Put business logic in routes/views
- Ignore N+1 queries
- Mix async and sync carelessly
### ✅ DO:
- Choose framework based on context
- Ask about async requirements
- Use Pydantic for validation
- Separate concerns (routes → services → repos)
- Test critical paths
---
> **Remember**: Python patterns are about decision-making for YOUR specific context. Don't copy code—think about what serves your application best.
This skill teaches Python development principles and decision-making for modern projects, focusing on framework selection, async vs sync, type hints, and project structure. It emphasizes reasoning over copying code and guides you to choose patterns that fit your context. Practical checklists and anti-pattern warnings help avoid common mistakes.
It asks the right questions about your project (API vs full-stack, concurrency needs, team familiarity) and maps answers to recommended frameworks and patterns. It explains when to prefer async or sync, which libraries to pick for async I/O, how to apply type hints and Pydantic, and how to structure projects by size. It also covers background task choices, error-handling patterns, and testing strategies to validate design decisions.
When should I force async across the codebase?
Don’t force async globally. Choose async for components that benefit from concurrency (I/O-bound). Keep CPU-bound parts synchronous and isolate with multiprocessing or worker services.
How strict should I be with type hints?
Be strict on public APIs, services, and class attributes. You can be pragmatic for local variables and one-off scripts to keep developer velocity.