Configure ruff linter rules in pyproject.toml with Python

Reads an existing pyproject.toml and merges common ruff linter rules into the tool.ruff section using Python's tomllib.

Easy Python 3.11+ Aug 9, 2026 Modern tooling 12 views 0 copies

Python code

45 lines
Python 3.11+
import tomllib
from pathlib import Path

def configure_ruff_linter_rules(project_path: str = ".") -> dict:
    """Add common ruff linter rules to pyproject.toml if missing."""
    pyproject_path = Path(project_path) / "pyproject.toml"
    
    # Default config for ruff linter with practical rules
    ruff_config = {
        "tool": {
            "ruff": {
                "line-length": 88,
                "target-version": "py39",
                "lint": {
                    "select": ["E", "F", "I", "UP", "B"],
                    "ignore": ["E501"],
                    "per-file-ignores": {"__init__.py": ["E402"]},
                },
            }
        }
    }
    
    if not pyproject_path.exists():
        return {"error": f"{pyproject_path} not found", "config": ruff_config}
    
    with pyproject_path.open("rb") as f:
        existing = tomllib.load(f)
    
    # Merge existing config with ruff defaults
    tool = existing.setdefault("tool", {})
    ruff = tool.setdefault("ruff", {})
    ruff.setdefault("line-length", 88)
    ruff.setdefault("target-version", "py39")
    lint = ruff.setdefault("lint", {})
    lint.setdefault("select", ["E", "F", "I", "UP", "B"])
    lint.setdefault("ignore", ["E501"])
    lint.setdefault("per-file-ignores", {"__init__.py": ["E402"]})
    
    return existing

if __name__ == "__main__":
    result = configure_ruff_linter_rules(".")
    print(result["tool"]["ruff"]["lint"]["select"])
    print(result["tool"]["ruff"]["lint"]["ignore"])
    print(result["tool"]["ruff"]["line-length"])

Output

stdout
['E', 'F', 'I', 'UP', 'B']
['E501']
88

How it works

The function uses tomllib (Python 3.11+) to parse the TOML file in binary mode, which is required by the library. It then uses setdefault on nested dictionaries to add each ruff setting only if it is not already present. Because setdefault returns the existing value when a key exists, the function preserves the user's original configuration. The resulting dictionary is returned as native Python objects, so no writing back to disk occurs in this example. The default rules select common error and style checks (E, F, I, UP, B) while ignoring long lines (E501) as a practical starting point.

Common mistakes

  • Opening the file in text mode instead of 'rb' — tomllib.load requires a binary file object
  • Using json, yaml, or configparser to parse TOML instead of tomllib or tomli
  • Overwriting user settings by assigning directly instead of using setdefault
  • Running the script in Python 3.10 or earlier without installing the tomli backport

Variations

  1. Use `tomli` for Python 3.10 and earlier: `import tomli; with open(file, 'rb') as f: data = tomli.load(f)`
  2. Write the merged config back to disk with `tomllib.dump` (Python 3.11+) instead of just returning it

Real-world use cases

  • A setup script that bootstraps a new Python project with sensible ruff defaults.
  • A CI preflight step that ensures a consistent linting baseline across multiple repos.
  • A CLI tool that upgrades an existing project to modern ruff-based linting rules automatically.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Modern tooling

Related tutorials and quizzes for this topic.