Development Guide
Coding standards, workflow, and best practices for this template.
Code Style
Python style
- Follow [PEP 8]; auto-format with Black (88 cols)
- Meaningful names; add type hints
- Google-style docstrings
Example:
def calculate_total(items: list[dict[str, float]], tax_rate: float = 0.1) -> float:
"""
Calculate the total price including tax.
Args:
items: List of items with 'price'
tax_rate: Tax rate as decimal
Returns:
Total price including tax
Raises:
ValueError: If any item is negative
"""
if not items:
return 0.0
subtotal = sum(item["price"] for item in items)
if subtotal < 0:
raise ValueError("Total cannot be negative")
return subtotal * (1 + tax_rate)
Naming
- Files/dirs: lowercase, underscores (
my_module.py); teststest_module.py - Vars/functions:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE - Private:
_internal,__for_mangling
Docstrings
Use Google style for functions and classes (see examples above and below).
Type Hints
Basic types and containers:
Optional and unions:
from typing import Optional, Union
def find_user(user_id: str) -> Optional[dict]:
"""Find user by ID."""
...
def process_value(value: Union[int, str]) -> str:
"""Process integer or string value."""
return str(value)
Generics:
from typing import TypeVar, List
T = TypeVar("T")
def reverse(items: List[T]) -> List[T]:
"""Reverse a list."""
return items[::-1]
Git Workflow
Branch naming
feature/add-user-authentication
bugfix/fix-login-error
hotfix/critical-security-patch
refactor/improve-api-structure
docs/update-readme
test/add-integration-tests
Conventional Commits
Format:
Types: feat, fix, docs, style, refactor, test, chore.
Example:
git commit -m "feat: add user authentication"
git commit -m "feat(auth): add OAuth2 login support
- Implement OAuth2 flow
- Add token refresh
- Update session management
Closes #123"
Pre-commit checks
Automatically run before commit: 1. black 2. ruff 3. flake8 4. mypy 5. interrogate 6. isort
Skip (not recommended):
Testing Standards
Layout
tests/
├── __init__.py
├── conftest.py
├── test_core.py
├── test_api.py
└── integration/
└── test_integration.py
Writing tests
import pytest
from src.core.example import Example
class TestExample:
"""Test suite for Example."""
def test_init(self):
example = Example(42)
assert example.value == 42
def test_method_with_valid_input(self):
example = Example(10)
assert example.calculate(2) == 20
def test_method_with_invalid_input(self):
example = Example(10)
with pytest.raises(ValueError):
example.calculate(-1)
@pytest.mark.parametrize("input_value,expected", [(0, 0), (5, 25), (10, 100)])
def test_calculate_multiple(self, input_value, expected):
example = Example(input_value)
assert example.calculate(input_value) == expected
Coverage targets
- Minimum: 80%
- Core logic: 100%
- Check doc coverage with
interrogate
Code Review Checklist
- Functionality: meets requirements; no regressions; error handling present
- Quality: follows style; lint passes; formatted; no type errors
- Tests: unit tests added; passing; coverage ok; integration tests as needed
- Docs: docstrings added; README/CHANGELOG updated if needed
- Other: no secrets; no temp files; CI green
Performance
- Prefer generators; cache results; choose right data structures; async I/O when helpful
def process_large_dataset(data):
"""Use generator for large data."""
for item in data:
yield process_item(item)
Profile with snakeviz:
import cProfile
def profile_code(func):
"""Profile a function."""
profiler = cProfile.Profile()
profiler.enable()
result = func()
profiler.disable()
profiler.dump_stats("profile.stats")
return result
Best Practices
Error handling
import logging
logger = logging.getLogger(__name__)
def process_user_data(user_id: str) -> dict:
"""Process user data with proper error handling."""
try:
user = get_user(user_id)
return process(user)
except UserNotFoundError:
logger.error("User %s not found", user_id)
raise
except Exception as exc:
logger.error("Unexpected error processing user %s: %s", user_id, exc)
raise ProcessingError(f"Failed to process user: {exc}") from exc
Logging
import logging
logger = logging.getLogger(__name__)
def important_operation(value: str) -> None:
"""Perform important operation with logging."""
logger.info("Starting important operation with value: %s", value)
try:
result = do_something(value)
logger.info("Operation completed successfully")
except Exception as exc:
logger.error("Operation failed: %s", exc, exc_info=True)
raise
Resource management
from contextlib import contextmanager
@contextmanager
def managed_resource(resource):
"""Context manager for resource management."""
try:
resource.open()
yield resource
finally:
resource.close()
Next Steps
- Read Quick Start
- See Tooling
- Check Configuration