| name | python |
| description | Python development with type hints, async patterns, testing, and modern best practices |
| category | languages |
| triggers | ["python","py","pip","pydantic","fastapi","django"] |
Python
Modern Python development following industry best practices. This skill covers type hints, async programming, data validation, testing, and production-ready patterns used by top engineering teams.
Purpose
Write clean, maintainable Python code:
- Use type hints for better code quality
- Implement async patterns for I/O operations
- Validate data with Pydantic
- Structure projects properly
- Write comprehensive tests
- Follow PEP standards
Features
1. Type Hints and Annotations
from typing import Optional, List, Dict, Union, Callable, TypeVar, Generic
from dataclasses import dataclass
from datetime import datetime
def greet(name: str) -> str:
return f"Hello, {name}"
def process_items(items: List[str], limit: int = 10) -> Dict[str, int]:
return {item: len(item) for item in items[:limit]}
def find_user(user_id: str) -> Optional[User]:
return db.users.get(user_id)
def parse_input(value: Union[str, int]) -> str:
return str(value)
Handler = Callable[[str, int], bool]
def register_handler(name: str, handler: Handler) -> None:
handlers[name] = handler
T = TypeVar('T')
class Repository(Generic[T]):
def __init__(self, model: type[T]) -> None:
self.model = model
def find_by_id(self, id: str) -> Optional[T]:
...
def find_all(self) -> List[T]:
...
def create(self, data: Dict) -> T:
...
UserId = str
UserMap = Dict[UserId, User]
EventHandler = Callable[[Event], None]
2. Dataclasses and Pydantic
from dataclasses import dataclass, field
from typing import Optional, List
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field, validator
from enum import Enum
@dataclass
class User:
id: str
email: str
name: str
created_at: datetime = field(default_factory=datetime.now)
roles: List[str] = field(default_factory=list)
@dataclass(frozen=True)
class Point:
x: float
y: float
def distance_to(self, other: 'Point') -> float:
return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
class UserRole(str, Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"
class UserCreate():
email: EmailStr
password: = Field(..., min_length=)
name: = Field(..., min_length=, max_length=)
role: UserRole = UserRole.USER
() -> :
(c.isupper() c v):
ValueError()
(c.isdigit() c v):
ValueError()
v
:
str_strip_whitespace =
():
:
email:
name:
role: UserRole
created_at: datetime
:
from_attributes =
(BaseModel, [T]):
data: [T]
total:
page:
limit:
has_more:
3. Async Programming
import asyncio
from typing import List, Dict, Any
import aiohttp
import asyncpg
async def fetch_data(url: str) -> Dict[str, Any]:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()
async def fetch_all(urls: List[str]) -> List[Dict[str, Any]]:
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
return await asyncio.gather(*tasks)
async def fetch_url(session: aiohttp.ClientSession, url: str) -> Dict[str, Any]:
async with session.get(url) response:
response.json()
:
():
.dsn = dsn
.pool: [asyncpg.Pool] =
() -> :
.pool = asyncpg.create_pool(
.dsn,
min_size=,
max_size=,
)
() -> :
.pool:
.pool.close()
() -> []:
.pool.acquire() conn:
row = conn.fetchrow(
,
user_id
)
(row) row
() -> :
.pool.acquire() conn:
row = conn.fetchrow(
,
email, name
)
(row)
:
() -> :
.setup()
() -> :
.cleanup()
:
():
.rate = rate
.per = per
.semaphore = asyncio.Semaphore(rate)
() -> :
.semaphore.acquire()
asyncio.create_task(._release())
() -> :
asyncio.sleep(.per)
.semaphore.release()
4. Error Handling
from typing import TypeVar, Generic
from dataclasses import dataclass
class AppError(Exception):
def __init__(self, message: str, code: str, status_code: int = 500):
self.message = message
self.code = code
self.status_code = status_code
super().__init__(message)
class NotFoundError(AppError):
def __init__(self, resource: str, id: str):
super().__init__(
f"{resource} with id {id} not found",
"NOT_FOUND",
404
)
class ValidationError(AppError):
def __init__(self, field: str, message: str):
super().__init__(
f"Validation error on {field}: {message}",
"VALIDATION_ERROR",
)
T = TypeVar()
E = TypeVar(, bound=Exception)
([T]):
value: T
() -> :
() -> :
([E]):
error: E
() -> :
() -> :
Result = Ok[T] | Err[E]
() -> Result[, ValueError]:
b == :
Err(ValueError())
Ok(a / b)
result = divide(, )
result.is_ok():
()
:
()
5. Testing with Pytest
import pytest
from unittest.mock import Mock, patch, AsyncMock
from datetime import datetime
def test_create_user():
user = create_user("test@example.com", "Test User")
assert user.email == "test@example.com"
assert user.name == "Test User"
@pytest.mark.parametrize("email,expected", [
("valid@example.com", True),
("invalid-email", False),
("", False),
])
def test_validate_email(email: str, expected: bool):
assert validate_email(email) == expected
@pytest.fixture
def user():
return User(
id="test-id",
email="test@example.com",
name="Test User"
)
@pytest.fixture
def db():
db = Database(":memory:")
db.connect()
yield db
db.disconnect()
def test_user_creation(user: User):
user. ==
():
db = AsyncMock()
db.fetch_user.return_value = {: , : }
result = db.fetch_user()
result[] ==
():
patch() mock_get:
mock_get.return_value.json.return_value = {: }
result = fetch_external_data()
result == {: }
mock_get.assert_called_once()
():
pytest.raises(NotFoundError) exc_info:
find_user()
(exc_info.value)
:
():
.service = UserService()
.mock_repo = Mock()
.service.repo = .mock_repo
():
.mock_repo.create.return_value = User(=, email=)
user = .service.create_user(, )
user.email ==
():
.mock_repo.find_by_email.return_value = User(=, email=)
pytest.raises(ValidationError):
.service.create_user(, )
6. Project Structure
project/
├── src/
│ └── myapp/
│ ├── __init__.py
│ ├── main.py
│ ├── config.py
│ ├── models/
│ │ ├── __init__.py
│ │ └── user.py
│ ├── services/
│ │ ├── __init__.py
│ │ └── user_service.py
│ ├── repositories/
│ │ ├── __init__.py
│ │ └── user_repository.py
│ └── api/
│ ├── __init__.py
│ ├── routes.py
│ └── dependencies.py
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ ├── unit/
│ │ └── test_user_service.py
│ └── integration/
│ └── test_api.py
├── pyproject.toml
├── requirements.txt
└── README.md
7. Configuration Management
from pydantic_settings import BaseSettings
from functools import lru_cache
from typing import Optional
class Settings(BaseSettings):
app_name: str = "MyApp"
debug: bool = False
database_url: str
redis_url: Optional[str] = None
secret_key: str
jwt_algorithm: str = "HS256"
jwt_expire_minutes: int = 30
cors_origins: list[str] = ["http://localhost:3000"]
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
@lru_cache
def get_settings() -> Settings:
return Settings()
settings = get_settings()
print(settings.database_url)
Use Cases
FastAPI Application
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
app = FastAPI()
@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: str, db: Session = Depends(get_db)):
user = await db.users.find_by_id(user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(data: UserCreate, db: Session = Depends(get_db)):
existing = await db.users.find_by_email(data.email)
if existing:
raise HTTPException(status_code=400, detail="Email already exists")
return await db.users.create(data.dict())
CLI Application
import click
@click.group()
def cli():
"""My CLI application."""
pass
@cli.command()
@click.option('--name', prompt='Your name', help='User name')
@click.option('--count', default=1, help='Number of greetings')
def hello(name: str, count: int):
"""Greet the user."""
for _ in range(count):
click.echo(f'Hello, {name}!')
if __name__ == '__main__':
cli()
Best Practices
Do's
- Use type hints everywhere
- Use dataclasses or Pydantic for data
- Use async for I/O operations
- Follow PEP 8 and PEP 257
- Use virtual environments
- Write comprehensive docstrings
- Use pytest for testing
Don'ts
- Don't use mutable default arguments
- Don't catch bare exceptions
- Don't ignore type errors
- Don't use global state
- Don't skip error handling
- Don't write untestable code
References