Python 3.11+ development with type hints, async patterns, FastAPI, and pytest. Use for backend services, CLI tools, data processing, and API development.
Python 3.11+ development with type hints, async/await, FastAPI, and pytest. Use for backend services, CLI tools, data processing, and API development.
/plugin marketplace add FortiumPartners/ensemble/plugin install ensemble-development@ensembleThis skill inherits all available tools. When active, it can use any tool Claude has access to.
README.mdREFERENCE.mdVALIDATION.mdexamples/README.mdexamples/fastapi_app.example.pyexamples/patterns.example.pytemplates/README.mdtemplates/cli.template.pytemplates/fastapi_router.template.pytemplates/module.template.pytemplates/pytest.template.pytemplates/service.template.pyPython 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.
Loaded by backend-developer when:
pyproject.toml or setup.py presentrequirements.txt with Python dependencies.py files in project root or src/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
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)
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.
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]
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.
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
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.
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=["*"])
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)]
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.
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])
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.
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
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()
@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.
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-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.
# 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/
[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"]
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.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.