Awesome-omni-skill python-patterns
Python patterns, type hints, async/await, and best practices for production code
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/python-patterns-jonathan0823" ~/.claude/skills/diegosouzapw-awesome-omni-skill-python-patterns-52dfe3 && rm -rf "$T"
manifest:
skills/development/python-patterns-jonathan0823/SKILL.mdsource content
Python Patterns Skill
Overview
This skill provides guidelines for writing production-ready Python code following modern Python best practices, type hints, async patterns, and framework-specific conventions for FastAPI, Django, and data processing.
Core Principles
1. Type Hints
Always use type hints for function signatures and complex data structures.
# DO: Use type hints for all function parameters and return types from typing import Optional, List, Dict, Union from dataclasses import dataclass def process_user(user_id: int, name: str, active: bool = True) -> Dict[str, Union[str, int, bool]]: """Process a user and return their data.""" return {"id": user_id, "name": name, "active": active} # DO: Use dataclasses for data containers @dataclass class User: id: int name: str email: str active: bool = True # DO: Use Optional for nullable types def find_user(user_id: int) -> Optional[User]: """Find a user by ID. Returns None if not found.""" # Implementation return None # DO: Use Union for multiple possible types def parse_value(value: Union[str, int, float]) -> float: """Parse a value to float.""" return float(value)
2. Error Handling
Use exceptions with proper context and custom exception classes.
# DO: Create custom exceptions for domain errors class ValidationError(Exception): """Raised when input validation fails.""" pass class NotFoundError(Exception): """Raised when a resource is not found.""" pass # DO: Provide context in error messages def get_user(user_id: int) -> User: """Get a user by ID.""" try: user = database.get(user_id) except DatabaseError as e: raise NotFoundError(f"User with ID {user_id} not found") from e if user is None: raise NotFoundError(f"User with ID {user_id} not found") return user # DO: Use try-except-finally for resource cleanup def process_file(filename: str) -> str: """Process a file and return its contents.""" file = None try: file = open(filename, 'r') return file.read() except FileNotFoundError: raise ValidationError(f"File {filename} not found") finally: if file: file.close() # BETTER: Use context managers from pathlib import Path def process_file(filename: str) -> str: """Process a file and return its contents.""" try: return Path(filename).read_text() except FileNotFoundError: raise ValidationError(f"File {filename} not found")
3. Async/Await Patterns
Use asyncio for concurrent operations.
import asyncio from typing import List import aiohttp # DO: Use async/await for I/O operations async def fetch_data(url: str) -> dict: """Fetch data from a URL asynchronously.""" async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.json() # DO: Use asyncio.gather for concurrent operations async def fetch_multiple(urls: List[str]) -> List[dict]: """Fetch data from multiple URLs concurrently.""" tasks = [fetch_data(url) for url in urls] return await asyncio.gather(*tasks) # DO: Use async context managers class DatabaseConnection: async def __aenter__(self): self.conn = await create_connection() return self.conn async def __aexit__(self, exc_type, exc_val, exc_tb): await self.conn.close() async def query_database(): async with DatabaseConnection() as conn: result = await conn.execute("SELECT * FROM users") return result # DO: Handle async iteration async def process_items(items: List[dict]): """Process items asynchronously.""" for item in items: await process_single_item(item)
FastAPI Patterns
1. Project Structure
myapp/ ├── app/ │ ├── __init__.py │ ├── main.py # Application entry point │ ├── config.py # Configuration settings │ ├── models/ # Database models │ ├── schemas/ # Pydantic schemas │ ├── routers/ # API route handlers │ ├── services/ # Business logic │ ├── dependencies.py # FastAPI dependencies │ └── utils/ # Utility functions ├── tests/ ├── requirements.txt └── Dockerfile
2. Dependency Injection
from fastapi import FastAPI, Depends, HTTPException from sqlalchemy.orm import Session from typing import Generator app = FastAPI() # DO: Use dependency injection for database sessions def get_db() -> Generator[Session, None, None]: """Get database session.""" db = SessionLocal() try: yield db finally: db.close() # DO: Create reusable dependencies async def get_current_user(token: str = Depends(oauth2_scheme)) -> User: """Get current authenticated user.""" user = await verify_token(token) if not user: raise HTTPException(status_code=401, detail="Invalid token") return user @app.get("/users/me") async def read_current_user(current_user: User = Depends(get_current_user)): return current_user # DO: Use dependencies for common parameters from fastapi import Query async def pagination_params( skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000) ): return {"skip": skip, "limit": limit} @app.get("/items/") async def read_items(pagination: dict = Depends(pagination_params)): return {"skip": pagination["skip"], "limit": pagination["limit"]}
3. Pydantic Schemas
from pydantic import BaseModel, Field, validator from typing import Optional from datetime import datetime # DO: Use Pydantic for request/response validation class UserBase(BaseModel): email: str = Field(..., regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') name: str = Field(..., min_length=1, max_length=100) class UserCreate(UserBase): password: str = Field(..., min_length=8) @validator('password') def validate_password(cls, v): if not any(c.isupper() for c in v): raise ValueError('Password must contain at least one uppercase letter') if not any(c.isdigit() for c in v): raise ValueError('Password must contain at least one digit') return v class UserResponse(UserBase): id: int created_at: datetime class Config: orm_mode = True # DO: Use response models tags=["users"]) async def create_user(user: UserCreate, db: Session = Depends(get_db)): db_user = User(**user.dict()) db.add(db_user) db.commit() db.refresh(db_user) return db_user
Django Patterns
1. Project Structure
myproject/ ├── config/ # Project configuration │ ├── __init__.py │ ├── settings/ │ │ ├── __init__.py │ │ ├── base.py # Base settings │ │ ├── development.py │ │ └── production.py │ ├── urls.py │ └── wsgi.py ├── apps/ # Django applications │ ├── users/ │ ├── blog/ │ └── common/ ├── templates/ ├── static/ ├── media/ ├── requirements/ │ ├── base.txt │ ├── development.txt │ └── production.txt └── manage.py
2. Models
from django.db import models from django.contrib.auth.models import AbstractUser from django.core.validators import EmailValidator # DO: Use custom User model class User(AbstractUser): """Custom user model.""" email = models.EmailField(unique=True, validators=[EmailValidator]) bio = models.TextField(blank=True) avatar = models.ImageField(upload_to='avatars/', blank=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] # DO: Use model managers for complex queries class ArticleManager(models.Manager): def published(self): return self.filter(status='published', published_at__isnull=False) def by_author(self, author): return self.filter(author=author) class Article(models.Model): STATUS_CHOICES = [ ('draft', 'Draft'), ('published', 'Published'), ('archived', 'Archived'), ] title = models.CharField(max_length=200) content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='articles') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft') published_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) objects = ArticleManager() class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['status', 'published_at']), ] def __str__(self): return self.title
3. Views and Serializers (DRF)
from rest_framework import viewsets, serializers, permissions from rest_framework.decorators import action from rest_framework.response import Response # DO: Use ModelSerializer class ArticleSerializer(serializers.ModelSerializer): author_name = serializers.CharField(source='author.username', read_only=True) class Meta: model = Article fields = ['id', 'title', 'content', 'author', 'author_name', 'status', 'published_at'] read_only_fields = ['author'] # DO: Use ViewSets for CRUD operations class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly] def get_queryset(self): """Filter articles based on query parameters.""" queryset = Article.objects.all() status = self.request.query_params.get('status') if status: queryset = queryset.filter(status=status) return queryset def perform_create(self, serializer): """Set the author to the current user.""" serializer.save(author=self.request.user) @action(detail=True, methods=['post']) def publish(self, request, pk=None): """Custom action to publish an article.""" article = self.get_object() article.status = 'published' article.save() return Response({'status': 'article published'})
Testing Patterns
1. pytest Best Practices
import pytest from unittest.mock import Mock, patch from fastapi.testclient import TestClient # DO: Use fixtures for setup @pytest.fixture def client(): """Create test client.""" from app.main import app return TestClient(app) @pytest.fixture def mock_db(): """Mock database session.""" return Mock() # DO: Use parametrize for multiple test cases @pytest.mark.parametrize("input_value,expected", [ ("hello", 5), ("world", 5), ("", 0), ]) def test_string_length(input_value, expected): assert len(input_value) == expected # DO: Mock external dependencies @patch('app.services.email_service.send_email') def test_user_registration(mock_send_email, client): """Test user registration with mocked email.""" response = client.post("/users/", json={ "email": "test@example.com", "password": "SecurePass123" }) assert response.status_code == 201 mock_send_email.assert_called_once() # DO: Test async code @pytest.mark.asyncio async def test_async_function(): result = await async_operation() assert result is not None
2. Test Structure
# DO: Group tests in classes class TestUserService: """Test suite for user service.""" def test_create_user(self, mock_db): """Test creating a new user.""" # Arrange user_data = {"email": "test@example.com", "password": "pass123"} # Act user = UserService.create_user(mock_db, user_data) # Assert assert user.email == user_data["email"] mock_db.add.assert_called_once() def test_create_user_duplicate_email(self, mock_db): """Test creating user with duplicate email raises error.""" # Arrange mock_db.query.return_value.filter.return_value.first.return_value = Mock() # Act & Assert with pytest.raises(ValidationError, match="Email already exists"): UserService.create_user(mock_db, {"email": "exists@example.com"})
Data Processing Patterns
1. Pandas Best Practices
import pandas as pd import numpy as np from typing import Optional # DO: Use type hints with pandas def process_sales_data(df: pd.DataFrame) -> pd.DataFrame: """Process sales data and return cleaned dataframe.""" # Remove duplicates df = df.drop_duplicates() # Handle missing values df['revenue'] = df['revenue'].fillna(0) # Type conversion df['date'] = pd.to_datetime(df['date']) return df # DO: Use vectorized operations (much faster than loops) def calculate_metrics(df: pd.DataFrame) -> pd.DataFrame: """Calculate business metrics.""" # Vectorized operation df['profit_margin'] = (df['revenue'] - df['cost']) / df['revenue'] # DON'T: Use iterrows() for simple calculations # for idx, row in df.iterrows(): # df.loc[idx, 'profit_margin'] = (row['revenue'] - row['cost']) / row['revenue'] return df # DO: Use method chaining def clean_and_transform(df: pd.DataFrame) -> pd.DataFrame: """Clean and transform data using method chaining.""" return (df .dropna(subset=['user_id']) .assign(revenue=lambda x: x['price'] * x['quantity']) .query('revenue > 0') .sort_values('revenue', ascending=False) .reset_index(drop=True) )
Performance Best Practices
1. Use Generators for Large Data
from typing import Iterator # DO: Use generators for memory efficiency def read_large_file(filepath: str) -> Iterator[str]: """Read large file line by line.""" with open(filepath, 'r') as f: for line in f: yield line.strip() # Usage for line in read_large_file('large_file.txt'): process_line(line) # DO: Use generator expressions squares = (x**2 for x in range(1000000)) # Generator # vs squares_list = [x**2 for x in range(1000000)] # List (uses more memory)
2. Caching
from functools import lru_cache import functools # DO: Use LRU cache for expensive operations @lru_cache(maxsize=128) def fibonacci(n: int) -> int: """Calculate fibonacci number with caching.""" if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) # DO: Use cache for API calls import requests from functools import wraps import time def cache_with_ttl(seconds: int): """Decorator to cache function results with TTL.""" def decorator(func): cache = {} @wraps(func) def wrapper(*args, **kwargs): key = str(args) + str(kwargs) if key in cache: result, timestamp = cache[key] if time.time() - timestamp < seconds: return result result = func(*args, **kwargs) cache[key] = (result, time.time()) return result return wrapper return decorator @cache_with_ttl(seconds=300) def fetch_exchange_rate(currency: str) -> float: """Fetch exchange rate with 5-minute cache.""" response = requests.get(f"https://api.example.com/rates/{currency}") return response.json()['rate']
When to Use This Skill
Use this skill when:
- Writing or reviewing Python code
- Setting up FastAPI or Django projects
- Implementing async/await patterns
- Processing data with Pandas
- Writing tests with pytest
- Optimizing Python performance
Related Skills
- General testing patterns@testing-strategies
- API design principles@api-rest-design
- Database patterns@postgresql-patterns
- Feature development workflow@feature-development