developing-with-python
Python 3.11+ development with type hints, async patterns, FastAPI, and pytest. Use for backend services, CLI tools, data processing, and API development.
When & Why to Use This Skill
This Claude skill empowers developers to master modern Python 3.11+ development, focusing on building high-performance backend services and scalable applications. It integrates industry best practices such as strict type hinting, asynchronous programming patterns, and robust API development using FastAPI. By providing structured templates for project layouts, Pydantic data validation, and comprehensive testing with pytest, it ensures the delivery of maintainable, type-safe, and production-ready code.
Use Cases
- Architecting and deploying high-performance RESTful APIs using FastAPI with automated Pydantic schema validation.
- Implementing efficient asynchronous data processing pipelines and concurrent task management using async/await and semaphores.
- Establishing standardized project structures (src-layout) with modern pyproject.toml configuration for professional software engineering.
- Developing comprehensive test suites using pytest, including async fixtures and mocking for backend service verification.
- Refactoring legacy Python codebases to leverage modern 3.11+ features like advanced type hints, dataclasses, and improved error handling.
| name | developing-with-python |
|---|---|
| description | Python 3.11+ development with type hints, async patterns, FastAPI, and pytest. Use for backend services, CLI tools, data processing, and API development. |
Python Development Skill
Python 3.11+ development with modern patterns including type hints, async/await, FastAPI, and pytest.
Progressive Disclosure: This file provides quick reference patterns. For comprehensive guides, see REFERENCE.md.
Table of Contents
- When to Use
- Quick Start
- Project Structure
- Type Hints
- Dataclasses & Pydantic
- FastAPI Patterns
- Async Patterns
- Testing with pytest
- Error Handling
- Anti-Patterns
- CLI Commands
- Configuration
- See Also
When to Use
Loaded by backend-developer when:
pyproject.tomlorsetup.pypresentrequirements.txtwith Python dependencies.pyfiles in project root orsrc/- FastAPI, Django, Flask detected
Quick Start
Basic Module
from __future__ import annotations
from dataclasses import dataclass
from typing import TypeVar, Generic
T = TypeVar("T")
@dataclass
class Result(Generic[T]):
value: T
success: bool = True
error: str | None = None
@classmethod
def ok(cls, value: T) -> Result[T]:
return cls(value=value, success=True)
@classmethod
def fail(cls, error: str) -> Result[T]:
return cls(value=None, success=False, error=error) # type: ignore
FastAPI Endpoint
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI(title="My API", version="1.0.0")
class UserCreate(BaseModel):
email: str = Field(..., pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$")
name: str = Field(..., min_length=1, max_length=100)
class UserResponse(BaseModel):
id: int
email: str
name: str
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate) -> UserResponse:
return UserResponse(id=1, email=user.email, name=user.name)
Project Structure
Standard Layout (src-layout)
my_project/
├── src/
│ └── my_package/
│ ├── __init__.py
│ ├── main.py # Entry point
│ ├── config.py # Configuration
│ ├── models/ # Data models
│ ├── services/ # Business logic
│ ├── repositories/ # Data access
│ └── api/ # API layer
│ ├── routes/
│ └── dependencies.py
├── tests/
│ ├── conftest.py # Shared fixtures
│ ├── unit/
│ └── integration/
├── pyproject.toml
└── .python-version
More layouts: See REFERENCE.md#project-structure for FastAPI, Django, and CLI layouts.
Type Hints
Essential Types
from typing import Optional, Any
from collections.abc import Sequence, Mapping, Callable
# Basic types
name: str = "Alice"
age: int = 30
score: float = 95.5
# Optional (Python 3.10+)
middle_name: str | None = None
# Collections
names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 95}
coordinates: tuple[float, float] = (1.0, 2.0)
# Abstract types (prefer for function parameters)
def process_items(items: Sequence[str]) -> list[str]:
return [item.upper() for item in items]
Function Signatures
from collections.abc import Callable
from typing import TypeVar, ParamSpec
T = TypeVar("T")
P = ParamSpec("P")
# Basic function
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
# Async function
async def fetch_user(user_id: int) -> dict[str, Any]:
...
# Generic decorator (preserves signature)
def logged(func: Callable[P, T]) -> Callable[P, T]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
More types: See REFERENCE.md#type-hints for Generics, Protocols, NewType.
Dataclasses & Pydantic
Dataclasses
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class User:
id: int
email: str
name: str
created_at: datetime = field(default_factory=datetime.now)
roles: list[str] = field(default_factory=list)
@dataclass(frozen=True) # Immutable
class Point:
x: float
y: float
Pydantic Models (FastAPI)
from pydantic import BaseModel, Field, field_validator, ConfigDict
class UserBase(BaseModel):
email: str = Field(..., pattern=r"^[\w\.-]+@[\w\.-]+\.\w+$")
name: str = Field(..., min_length=1, max_length=100)
class UserCreate(UserBase):
password: str = Field(..., min_length=8)
@field_validator("password")
@classmethod
def password_strength(cls, v: str) -> str:
if not any(c.isupper() for c in v):
raise ValueError("Must contain uppercase")
return v
class UserResponse(UserBase):
model_config = ConfigDict(from_attributes=True)
id: int
is_active: bool = True
More patterns: See REFERENCE.md#classes-and-data-classes for ABCs, Protocols.
FastAPI Patterns
Application Setup
from contextlib import asynccontextmanager
from collections.abc import AsyncIterator
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
await create_tables() # Startup
yield
await engine.dispose() # Shutdown
app = FastAPI(title="My API", lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])
Dependency Injection
from typing import Annotated
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_db() -> AsyncIterator[AsyncSession]:
async with get_session() as session:
yield session
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[AsyncSession, Depends(get_db)],
) -> User:
# Verify token and return user
...
# Type aliases for reuse
CurrentUser = Annotated[User, Depends(get_current_user)]
DbSession = Annotated[AsyncSession, Depends(get_db)]
Router Pattern
from fastapi import APIRouter, HTTPException, status, Query
router = APIRouter()
@router.get("", response_model=list[UserResponse])
async def list_users(
db: DbSession,
skip: Annotated[int, Query(ge=0)] = 0,
limit: Annotated[int, Query(ge=1, le=100)] = 20,
) -> list[UserResponse]:
service = UserService(db)
return await service.list(skip=skip, limit=limit)
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: int, db: DbSession) -> UserResponse:
user = await UserService(db).get(user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
More FastAPI: See REFERENCE.md#fastapi-patterns for error handling, middleware.
Async Patterns
Basic Async
import asyncio
async def fetch_data(url: str) -> dict:
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.json()
async def process_batch(items: list[str]) -> list[dict]:
tasks = [fetch_data(item) for item in items]
return await asyncio.gather(*tasks)
async def process_with_limit(items: list[str], max_concurrent: int = 10) -> list[dict]:
semaphore = asyncio.Semaphore(max_concurrent)
async def limited_fetch(url: str) -> dict:
async with semaphore:
return await fetch_data(url)
return await asyncio.gather(*[limited_fetch(item) for item in items])
Async Context Managers
from contextlib import asynccontextmanager
@asynccontextmanager
async def database_transaction(db: AsyncSession) -> AsyncIterator[AsyncSession]:
try:
yield db
await db.commit()
except Exception:
await db.rollback()
raise
More async: See REFERENCE.md#async-await-patterns for generators, streaming.
Testing with pytest
Basic Tests
import pytest
from unittest.mock import AsyncMock
class TestUserService:
@pytest.fixture
def service(self, mock_db: AsyncMock) -> UserService:
return UserService(mock_db)
async def test_get_user_found(self, service: UserService) -> None:
expected = User(id=1, email="test@example.com", name="Test")
service.repo.get.return_value = expected
result = await service.get(1)
assert result == expected
async def test_get_user_not_found(self, service: UserService) -> None:
service.repo.get.return_value = None
result = await service.get(999)
assert result is None
Fixtures (conftest.py)
import pytest
import pytest_asyncio
from httpx import AsyncClient, ASGITransport
@pytest_asyncio.fixture
async def async_client() -> AsyncClient:
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
yield client
@pytest.fixture
def override_deps(test_db: AsyncSession):
app.dependency_overrides[get_session] = lambda: test_db
yield
app.dependency_overrides.clear()
Parametrized Tests
@pytest.mark.parametrize("email,valid", [
("user@example.com", True),
("invalid", False),
])
def test_email_validation(email: str, valid: bool) -> None:
if valid:
user = User(id=1, email=email, name="Test")
assert user.email == email
else:
with pytest.raises(ValueError):
User(id=1, email=email, name="Test")
More testing: See REFERENCE.md#testing-with-pytest for API tests, mocking.
Error Handling
Exception Hierarchy
class AppError(Exception):
def __init__(self, message: str, code: str | None = None) -> None:
self.message = message
self.code = code or self.__class__.__name__
super().__init__(message)
class NotFoundError(AppError):
def __init__(self, resource: str, identifier: str | int) -> None:
super().__init__(f"{resource} '{identifier}' not found", code="NOT_FOUND")
class ValidationError(AppError):
def __init__(self, field: str, message: str) -> None:
super().__init__(f"{field}: {message}", code="VALIDATION_ERROR")
More patterns: See REFERENCE.md#error-handling for Result pattern.
Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|---|---|
Mutable default def f(items=[]) |
Shared across calls | Use items: list | None = None |
Bare except: |
Catches KeyboardInterrupt | Catch specific exceptions |
| No context managers | Resources not cleaned | Use with/async with |
| Blocking in async | Blocks event loop | Use async I/O or executor |
More details: See REFERENCE.md#anti-patterns for examples.
CLI Commands
# Setup
python -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"
# Run FastAPI
uvicorn myapp.main:app --reload --port 8000
# Testing
pytest # Run all
pytest --cov=src --cov-report=html # With coverage
# Code quality
mypy src/ && ruff check src/ --fix && ruff format src/
Configuration
pyproject.toml (Essential)
[project]
name = "myapp"
version = "1.0.0"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.109.0",
"uvicorn[standard]>=0.27.0",
"pydantic>=2.5.0",
]
[project.optional-dependencies]
dev = ["pytest>=7.4.0", "pytest-asyncio>=0.23.0", "mypy>=1.8.0", "ruff>=0.1.0"]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
[tool.mypy]
python_version = "3.11"
strict = true
[tool.ruff]
target-version = "py311"
line-length = 88
select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]
pydantic-settings
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
app_name: str = "My App"
debug: bool = False
database_url: str
secret_key: str
settings = Settings()
More config: See REFERENCE.md#configuration for full examples.
See Also
- REFERENCE.md - Comprehensive guide with advanced patterns
- templates/ - Code generation templates