Skip to main content
Glama
python-development.mdc14.2 kB
--- description: "Python development: modern Python, async/await, FastAPI, Django, data science, and ML patterns" globs: ["**/*.py", "**/pyproject.toml", "**/requirements.txt", "**/setup.py", "**/*.ipynb", "**/Pipfile"] alwaysApply: false --- # Python Development Patterns Modern Python (3.10+) best practices for web development, data science, and general programming. ## CRITICAL: Agentic-First Python Development ### Pre-Development Verification (MANDATORY) Before writing ANY Python code: ``` 1. CHECK PYTHON INSTALLATION → run_terminal_cmd("python3 --version") → run_terminal_cmd("pip --version") → run_terminal_cmd("uv --version") # Modern package manager 2. VERIFY CURRENT VERSIONS (use web_search) → web_search("Python stable version December 2024") → web_search("FastAPI latest version 2024") → web_search("Django LTS version 2024") 3. CHECK EXISTING PROJECT → Does pyproject.toml/requirements.txt exist? Read it first! → Is there a virtual environment? → What dependencies are already installed? 4. VIRTUAL ENVIRONMENT (ALWAYS USE) → run_terminal_cmd("python3 -m venv .venv") → run_terminal_cmd("source .venv/bin/activate") ``` ### CLI-First Python Development **ALWAYS use CLI for package management:** ```bash # Virtual environment (ALWAYS create first) python3 -m venv .venv source .venv/bin/activate # Unix .venv\Scripts\activate # Windows # Modern: uv (10-100x faster than pip) uv venv uv pip install fastapi uvicorn uv pip sync requirements.txt # Traditional: pip pip install fastapi uvicorn pip install -r requirements.txt pip freeze > requirements.txt # Framework CLIs (use these for scaffolding) # Django django-admin startproject myproject python manage.py startapp myapp # FastAPI with cookiecutter pip install cookiecutter cookiecutter https://github.com/tiangolo/full-stack-fastapi-template # Poetry (if project uses it) poetry init poetry add fastapi poetry install ``` ### Post-Edit Verification After ANY Python code changes: ```bash # Type checking mypy src/ # Linting ruff check . ruff format --check . # Or older tools flake8 . black --check . # Tests pytest pytest --cov=src # Verify imports work python -c "import mypackage" ``` ### Common Python Syntax Traps (Avoid These!) ```python # WRONG: Mutable default argument def append_to(item, target=[]): # Bug! List is shared! target.append(item) return target # CORRECT: Use None as default def append_to(item, target=None): if target is None: target = [] target.append(item) return target # WRONG: Not using context managers for files f = open('file.txt') data = f.read() f.close() # Might not run if exception occurs! # CORRECT: Always use with statement with open('file.txt') as f: data = f.read() # WRONG: Bare except clause try: risky_operation() except: # Catches EVERYTHING including KeyboardInterrupt! pass # CORRECT: Catch specific exceptions try: risky_operation() except (ValueError, TypeError) as e: logger.error(f"Operation failed: {e}") # WRONG: Late binding in closures funcs = [lambda: i for i in range(3)] [f() for f in funcs] # Returns [2, 2, 2], not [0, 1, 2]! # CORRECT: Capture value as default argument funcs = [lambda i=i: i for i in range(3)] [f() for f in funcs] # Returns [0, 1, 2] # WRONG: Using is for value comparison if x is 1: # Works sometimes, but wrong! pass # CORRECT: Use == for values, is for identity if x == 1: # Value comparison pass if x is None: # Identity (OK for None, True, False) pass ``` ### Python Version-Specific Features Check Python version before using new features: ```python # Python 3.10+ Pattern Matching match command: case ["quit"]: return "Goodbye" case _: return "Unknown" # Python 3.9+ Built-in Generic Types def process(items: list[str]) -> dict[str, int]: pass # Python 3.8+ Walrus Operator if (n := len(data)) > 10: print(f"Processing {n} items") # Check version in code if needed import sys if sys.version_info >= (3, 10): # Use match statement else: # Use if/elif chain ``` --- ## Modern Python Syntax ### Type Hints ```python # Basic types def greet(name: str) -> str: return f"Hello, {name}" # Collections (Python 3.9+) def process(items: list[str]) -> dict[str, int]: return {item: len(item) for item in items} # Optional and Union def find_user(id: int) -> User | None: return db.get(id) # Generic types from typing import TypeVar, Generic T = TypeVar('T') class Repository(Generic[T]): def get(self, id: int) -> T | None: ... def save(self, item: T) -> T: ... ``` ### Pattern Matching (3.10+) ```python match command: case ["quit"]: return "Goodbye" case ["load", filename]: return load_file(filename) case ["save", filename, *options]: return save_file(filename, options) case _: return "Unknown command" # With guards match user: case User(role="admin"): return full_access() case User(role="user", verified=True): return limited_access() case _: return no_access() ``` ### Dataclasses ```python from dataclasses import dataclass, field from datetime import datetime @dataclass class User: id: int name: str email: str created_at: datetime = field(default_factory=datetime.now) tags: list[str] = field(default_factory=list) def __post_init__(self): self.email = self.email.lower() # Immutable @dataclass(frozen=True) class Config: host: str port: int ``` --- ## Async/Await Patterns ### Basic Async ```python import asyncio async def fetch_data(url: str) -> dict: async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.json() # Parallel execution async def fetch_all(urls: list[str]) -> list[dict]: tasks = [fetch_data(url) for url in urls] return await asyncio.gather(*tasks) # With error handling async def safe_fetch(url: str) -> dict | None: try: return await fetch_data(url) except aiohttp.ClientError as e: logger.error(f"Failed to fetch {url}: {e}") return None ``` ### Async Context Managers ```python from contextlib import asynccontextmanager @asynccontextmanager async def get_db_connection(): conn = await database.connect() try: yield conn finally: await conn.close() # Usage async with get_db_connection() as conn: result = await conn.fetch("SELECT * FROM users") ``` --- ## FastAPI Patterns ### Application Structure ``` app/ __init__.py main.py # FastAPI app instance config.py # Settings models/ # Pydantic models routers/ # Route handlers services/ # Business logic repositories/ # Data access dependencies.py # Dependency injection ``` ### Route Handlers ```python from fastapi import FastAPI, HTTPException, Depends, status from pydantic import BaseModel app = FastAPI() class UserCreate(BaseModel): name: str email: str class UserResponse(BaseModel): id: int name: str email: str class Config: from_attributes = True @app.post("/users", response_model=UserResponse, status_code=status.HTTP_201_CREATED) async def create_user( user: UserCreate, db: Database = Depends(get_db) ) -> UserResponse: db_user = await db.users.create(user.model_dump()) return db_user @app.get("/users/{user_id}", response_model=UserResponse) async def get_user(user_id: int, db: Database = Depends(get_db)): user = await db.users.get(user_id) if not user: raise HTTPException(status_code=404, detail="User not found") return user ``` ### Dependency Injection ```python from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") async def get_current_user(token: str = Depends(oauth2_scheme)) -> User: user = await verify_token(token) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials", ) return user async def get_admin_user(user: User = Depends(get_current_user)) -> User: if user.role != "admin": raise HTTPException(status_code=403, detail="Admin access required") return user ``` ### Pydantic Validation ```python from pydantic import BaseModel, EmailStr, Field, field_validator class UserCreate(BaseModel): name: str = Field(min_length=1, max_length=100) email: EmailStr age: int = Field(ge=0, le=150) @field_validator('name') @classmethod def name_must_not_be_empty(cls, v: str) -> str: if not v.strip(): raise ValueError('Name cannot be empty') return v.strip() ``` --- ## Django Patterns ### Model Design ```python from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): bio = models.TextField(blank=True) avatar = models.ImageField(upload_to='avatars/', null=True) class Post(models.Model): author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts') title = models.CharField(max_length=200) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['-created_at'] def __str__(self): return self.title ``` ### Views (Class-Based) ```python from django.views.generic import ListView, DetailView, CreateView from django.contrib.auth.mixins import LoginRequiredMixin class PostListView(ListView): model = Post paginate_by = 10 context_object_name = 'posts' class PostCreateView(LoginRequiredMixin, CreateView): model = Post fields = ['title', 'content'] def form_valid(self, form): form.instance.author = self.request.user return super().form_valid(form) ``` ### QuerySet Optimization ```python # Avoid N+1 queries posts = Post.objects.select_related('author').prefetch_related('comments') # Only fetch needed fields users = User.objects.only('id', 'name', 'email') # Efficient counting count = Post.objects.filter(published=True).count() # Bulk operations Post.objects.filter(draft=True).update(published=True) Post.objects.bulk_create([Post(title=t) for t in titles]) ``` --- ## Data Science Patterns ### Pandas Best Practices ```python import pandas as pd # Method chaining df = ( pd.read_csv('data.csv') .dropna(subset=['important_column']) .assign( new_col=lambda x: x['col1'] + x['col2'], date=lambda x: pd.to_datetime(x['date_str']) ) .query('value > 0') .groupby('category') .agg({'value': 'sum', 'count': 'size'}) .reset_index() ) # Efficient operations # Bad: iterating rows for idx, row in df.iterrows(): df.loc[idx, 'new'] = row['a'] + row['b'] # Good: vectorized df['new'] = df['a'] + df['b'] # Memory efficiency df = pd.read_csv('large.csv', dtype={'id': 'int32', 'category': 'category'}, usecols=['id', 'category', 'value'] ) ``` ### NumPy Patterns ```python import numpy as np # Vectorized operations result = np.sum(arr1 * arr2, axis=1) # Broadcasting normalized = (data - data.mean(axis=0)) / data.std(axis=0) # Efficient conditionals result = np.where(condition, value_if_true, value_if_false) ``` --- ## Testing with pytest ### Test Structure ```python import pytest from myapp import calculate_total, UserService class TestCalculateTotal: def test_empty_list_returns_zero(self): assert calculate_total([]) == 0 def test_sums_positive_numbers(self): assert calculate_total([1, 2, 3]) == 6 def test_raises_on_invalid_input(self): with pytest.raises(ValueError): calculate_total(None) # Fixtures @pytest.fixture def db_session(): session = create_test_session() yield session session.rollback() @pytest.fixture def user_service(db_session): return UserService(db_session) def test_create_user(user_service): user = user_service.create(name="Test", email="test@example.com") assert user.id is not None ``` ### Async Tests ```python import pytest @pytest.mark.asyncio async def test_fetch_user(): user = await fetch_user(1) assert user.name == "Expected Name" ``` ### Parametrized Tests ```python @pytest.mark.parametrize("input,expected", [ ([], 0), ([1], 1), ([1, 2, 3], 6), ([-1, 1], 0), ]) def test_sum(input, expected): assert sum(input) == expected ``` --- ## Error Handling ### Custom Exceptions ```python class AppError(Exception): """Base exception for application errors.""" pass class NotFoundError(AppError): """Resource not found.""" def __init__(self, resource: str, id: int): self.resource = resource self.id = id super().__init__(f"{resource} with id {id} not found") class ValidationError(AppError): """Validation failed.""" def __init__(self, field: str, message: str): self.field = field self.message = message super().__init__(f"{field}: {message}") ``` ### Context Managers ```python from contextlib import contextmanager @contextmanager def transaction(db): try: yield db db.commit() except Exception: db.rollback() raise ``` --- ## Package Management ### pyproject.toml (Modern Standard) ```toml [project] name = "myproject" version = "0.1.0" description = "My Python project" requires-python = ">=3.10" dependencies = [ "fastapi>=0.100.0", "pydantic>=2.0.0", "sqlalchemy>=2.0.0", ] [project.optional-dependencies] dev = [ "pytest>=7.0.0", "black>=23.0.0", "ruff>=0.1.0", ] [tool.ruff] line-length = 100 select = ["E", "F", "I"] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" ``` ### uv (Fast Package Manager) ```bash # Install dependencies uv pip install -r requirements.txt # Add dependency uv pip install fastapi # Create virtual environment uv venv # Sync dependencies uv pip sync requirements.txt ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/madebyaris/rakitui-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server