Skip to content

Config Template — pyproject.toml + Pre-Commit

Copy-paste baseline for a new Python project with full quality stack.

pyproject.toml

[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []

[dependency-groups]
dev = [
    "ruff>=0.11",
    "mypy>=1.15",
    "wemake-python-styleguide>=1.6",
    "flake8>=7.3",
    "pre-commit>=4.2",
    "pytest>=8.3",
    "pytest-cov>=6.1",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

# --- Ruff ---
[tool.ruff]
target-version = "py312"
line-length = 100
exclude = [".venv", "migrations", "__pycache__", "dist"]

[tool.ruff.lint]
select = ["E", "W", "F", "I", "UP", "B", "SIM", "S", "C4", "PTH", "RET", "ARG", "PL", "RUF"]
ignore = ["E501"]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101", "ARG001", "PLR2004"]

[tool.ruff.lint.isort]
known-first-party = ["my_project"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true

# --- mypy ---
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_configs = true
no_implicit_reexport = true

[[tool.mypy.overrides]]
module = ["tests.*"]
disallow_untyped_defs = false

# --- pytest ---
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra -q --strict-markers --strict-config --cov=src --cov-branch --cov-fail-under=100"
markers = ["slow: long-running tests", "integration: external dependencies"]
filterwarnings = ["error"]

# --- coverage ---
[tool.coverage.run]
source = ["src"]
branch = true
omit = ["*/migrations/*"]

[tool.coverage.report]
show_missing = true
fail_under = 100
exclude_lines = [
    "if TYPE_CHECKING:",
    "if __name__",
    "@overload",
    "raise NotImplementedError",
]

setup.cfg (wemake-python-styleguide)

[flake8]
select = WPS
max-line-length = 100
max-cognitive-score = 12
max-function-score = 8
max-module-members = 10
max-local-variables = 8
max-returns = 5
exclude = .venv,migrations,__pycache__,dist

per-file-ignores =
    tests/*.py: WPS226, WPS432

.pre-commit-config.yaml

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-toml
      - id: check-added-large-files
        args: [--maxkb=500]
      - id: detect-private-key

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.8
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.15.0
    hooks:
      - id: mypy
        additional_dependencies: [types-requests, types-PyYAML]

  - repo: https://github.com/PyCQA/flake8
    rev: "7.3.0"
    hooks:
      - id: flake8
        args: [--select=WPS]
        additional_dependencies: [wemake-python-styleguide]

Bootstrap Commands

uv init my-project && cd my-project
uv add --dev ruff mypy wemake-python-styleguide flake8 pre-commit pytest pytest-cov
uv run pre-commit install
uv run ruff check . --fix && uv run ruff format .
uv run flake8 . --select=WPS
uv run mypy .
uv run pytest

CI (GitHub Actions)

name: quality
on: [push, pull_request]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true
      - run: uv sync --locked
      - run: uv run ruff check .
      - run: uv run ruff format . --check
      - run: uv run flake8 . --select=WPS
      - run: uv run mypy .
      - run: uv run pytest --cov=src --cov-branch --cov-fail-under=100