This document defines the code quality standards, tooling configuration, and conventions used in the Trobz Python Template and all generated projects. All code must follow these standards before being committed.
{project}/
├── {package_name}/
│ ├── __init__.py # Package initialization
│ ├── main.py # Application entry point
│ └── settings.py # Configuration (services only)
├── tests/
│ └── test_*.py # Test files matching pattern
├── .github/
│ └── workflows/ # CI/CD workflows
├── Makefile # Build and development commands
├── pyproject.toml # Project configuration
├── ruff.toml # Linting and formatting config
├── ty.toml # Type checker config
├── .pre-commit-config.yaml # Pre-commit hooks
├── .gitignore # Git ignore rules
└── README.md # Project documentation
| Type | Pattern | Example |
|---|---|---|
| Python modules | snake_case | user_service.py, api_handlers.py |
| Test files | test_*.py |
test_user_service.py |
| Classes | PascalCase | UserService, ApiHandler |
| Functions | snake_case | get_user(), validate_email() |
| Constants | SCREAMING_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT |
| Private members | _leading_underscore |
_internal_cache, _helper() |
Keep files under 200 lines for maintainability:
"""Module docstring describing purpose."""
# Standard library imports
import os
import sys
from typing import Optional
# Third-party imports
import requests
from pydantic import BaseModel
# Local imports
from . import settings
# Constants
DEFAULT_TIMEOUT = 30
MAX_RETRIES = 3
# Classes
class MyClass:
"""Class docstring."""
pass
# Functions
def my_function():
"""Function docstring."""
pass
# Entry point
if __name__ == "__main__":
passPython Version: 3.10+ for generated projects, 3.12+ for template generator
Encoding: UTF-8 (no encoding declaration needed in Python 3)
Line Length: 88 characters (Ruff default, Black-compatible)
Indentation: 4 spaces (never tabs)
Configuration in ruff.toml:
line-length = 88
target-version = "py310"
[lint]
select = ["E", "F", "I", "N", "W"] # Error, Pyflakes, Import sort, Naming, Warnings
ignore = ["E203", "E501"]
[lint.isort]
profile = "black"| Code | Category | Description |
|---|---|---|
| E | Error | PEP 8 errors (whitespace, indentation) |
| F | PyFlakes | Logic errors (unused imports, undefined names) |
| I | isort | Import sorting and organization |
| N | pep8-naming | Naming convention violations |
| W | Warning | PEP 8 warnings (blank lines, line breaks) |
- E203: Whitespace before colon (conflicts with formatters)
- E501: Line too long (handled by formatter)
Order (enforced by isort):
- Future imports:
from __future__ import annotations - Standard library:
import os,from typing import ... - Third-party:
import requests,from pydantic import ... - Local:
from . import settings,from .models import User
Blank line separates each group.
from __future__ import annotations
import os
import sys
from typing import Optional
import requests
from pydantic import BaseModel
from . import settings
from .models import UserAll functions and methods must include type hints:
def get_user(user_id: int) -> Optional[User]:
"""Retrieve user by ID."""
# Implementation
pass
class UserService:
def create_user(self, name: str, email: str) -> User:
"""Create new user."""
pass
def list_users(self) -> list[User]:
"""List all users."""
passUse Google-style docstrings for classes and functions:
def calculate_total(items: list[int], tax_rate: float = 0.1) -> float:
"""Calculate total with tax.
Args:
items: List of item prices.
tax_rate: Tax percentage as decimal (default 0.1 for 10%).
Returns:
Total price including tax.
Raises:
ValueError: If tax_rate is negative.
"""
if tax_rate < 0:
raise ValueError("tax_rate must be non-negative")
return sum(items) * (1 + tax_rate)
class UserService:
"""Service for managing user operations.
Attributes:
db: Database connection.
cache: Optional cache instance.
"""
def __init__(self, db: Database, cache: Optional[Cache] = None):
"""Initialize UserService.
Args:
db: Database connection instance.
cache: Optional cache for performance.
"""
self.db = db
self.cache = cacheAutomatically applied via make check:
- Consistent quote style (prefer double quotes)
- Consistent spacing around operators
- Consistent import formatting
- Consistent string formatting
Static type verification configured in ty.toml. Run via make check:
make check # Runs ty type checkerFix type errors before committing.
All commits must follow conventional commit format:
<type>(<scope>): <subject>
<body>
<footer>
| Type | Meaning | Version Bump |
|---|---|---|
| feat | New feature | Minor (0.1.0) |
| fix | Bug fix | Patch (0.0.1) |
| refactor | Code restructuring (no behavior change) | None |
| test | Test additions or fixes | None |
| docs | Documentation changes | None |
| chore | Maintenance tasks | None |
| ci | CI/CD configuration | None |
| style | Formatting changes | None |
feat(auth): add JWT token support
fix(api): resolve race condition in user lookup
refactor(core): simplify data validation logic
docs: update README with examples
test: add coverage for edge cases
chore: update dependencies
- Use imperative mood ("add" not "added")
- First line under 72 characters
- Reference issues:
Closes #123 - Include motivation in body (why, not what)
- Mark breaking changes:
BREAKING CHANGE: description
feat(api): add webhook support
Implement webhook callbacks for event notifications.
Clients can now register webhooks for user and order events.
Closes #456
Mark breaking changes to trigger major version bump:
feat(auth)!: require OAuth2 for all endpoints
BREAKING CHANGE: Authentication now requires OAuth2 tokens.
Old API keys no longer work. Migrate to new auth scheme.
Automatically run checks before each commit. Configuration in .pre-commit-config.yaml:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v5.0.0"
hooks:
- id: check-case-conflict # Detect case conflicts in filenames
- id: check-merge-conflict # Detect merge conflict markers
- id: check-toml # Validate TOML syntax
- id: check-yaml # Validate YAML syntax
- id: end-of-file-fixer # Add newline at EOF
- id: trailing-whitespace # Remove trailing whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.14.4"
hooks:
- id: ruff-check # Lint and check
args: [--exit-non-zero-on-fix]
- id: ruff-format # Format code# Run once during setup
uv run pre-commit install
# Run manually on all files
uv run pre-commit run -a
# Run on staged files (automatic before commit)
uv run pre-commit runtests/
├── test_main.py
├── test_models.py
├── test_services.py
└── conftest.py # Shared fixtures
Test files follow pattern: test_*.py or *_test.py
- Target: > 80% code coverage
- Critical paths: 100% coverage required
- Excluded:
__main__blocks, debug code
"""Tests for user service module."""
import pytest
from app.services import UserService
from app.models import User
class TestUserService:
"""Tests for UserService class."""
@pytest.fixture
def service(self):
"""Create UserService instance for testing."""
return UserService()
def test_create_user(self, service):
"""Test user creation."""
user = service.create_user(name="John", email="john@example.com")
assert user.name == "John"
assert user.email == "john@example.com"
def test_create_user_invalid_email(self, service):
"""Test user creation with invalid email."""
with pytest.raises(ValueError):
service.create_user(name="John", email="invalid")
def test_standalone_function():
"""Test standalone function."""
result = some_function(42)
assert result == 84# Run all tests
make test
# Run specific test file
uv run pytest tests/test_main.py
# Run with coverage report
uv run pytest --cov=app
# Run with verbose output
uv run pytest -vBefore committing, verify:
- Code passes
make check(linting, formatting, type checking) - Code passes
make test(all tests pass, coverage > 80%) - Commit message follows conventional format
- Type hints on all functions and methods
- Docstrings on all public classes and functions
- No hardcoded values (use configuration)
- No commented-out code (delete or create issue)
- Imports organized and sorted
- Line length <= 88 characters
- No trailing whitespace
- Descriptive variable names (avoid single letters except loops)
# Good: Specific exceptions
try:
user = get_user(user_id)
except UserNotFoundError:
logger.error(f"User {user_id} not found")
raise
except ValueError as e:
logger.error(f"Invalid user data: {e}")
raise
# Bad: Generic exceptions
try:
user = get_user(user_id)
except Exception:
pass"""Logging configuration and usage."""
import logging
logger = logging.getLogger(__name__)
# Usage
logger.debug("Debug information")
logger.info("Informational message")
logger.warning("Warning message")
logger.error("Error occurred", exc_info=True)
logger.critical("Critical error")- Avoid N+1 queries: Load related data efficiently
- Cache appropriately: Use caching for expensive operations
- Index databases: Add indexes for frequently queried fields
- Use generators: For large datasets to save memory
- Profile before optimizing: Measure before making changes
- No secrets in code: Use environment variables
- Input validation: Always validate user input
- SQL injection prevention: Use parameterized queries
- XSS prevention: Escape output in templates
- CSRF protection: Implement anti-CSRF tokens
- Dependency updates: Keep dependencies current
- Secret scanning: Enable pre-commit secret detection
- Module docstrings at top of file
- Class docstrings describing purpose and attributes
- Function docstrings with args, returns, raises
- Complex logic commented with explanation
- No obvious comments (e.g.,
i = i + 1 # increment i)
- README with quick start
- Setup instructions
- Configuration guide
- API documentation
- Examples and use cases
- Troubleshooting guide
# Install dependencies and pre-commit hooks
make install
# Run code quality checks
make check
# Run tests
make test
# Build distribution
make build
# Run service (service projects only)
make serveAll commands must pass before commit:
make check # Linting, formatting, type checking
make test # All tests pass with coverage- PEP 8: https://pep8.org/
- Black Formatter: https://github.com/psf/black
- Ruff: https://docs.astral.sh/ruff/
- pytest: https://docs.pytest.org/
- Type Hints: https://docs.python.org/3/library/typing.html
- Conventional Commits: https://www.conventionalcommits.org/