--- name: python-patterns description: Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Teaches thinking, not copying. --- # Python Patterns > **πŸ’‘ MCP Tool Available**: Use **Context7**, **Tavily**, **BraveSearch**, or **Serper.dev** first; only if those fail, use **WebSearch** or **WebFetch** as needed. > 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=" 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.