How to Configure Ruff Linter and Formatter in pyproject.toml
I just installed Ruff in my Python project and wanted to customize its behavior. The defaults are sensible, but my project has specific coding standards that differ from Ruff’s out-of-the-box configuration. Here’s what I learned about configuring Ruff through pyproject.toml.
The Problem: Default Settings Don’t Match My Project
Ruff’s defaults work great for new projects - it uses 88 character line length (same as Black), applies a core set of linting rules, and doesn’t enforce docstring conventions. But my existing codebase uses 79 character lines, requires comprehensive docstrings, and has specific style requirements.
I needed to configure Ruff to match these standards instead of fighting against the defaults.
Understanding Ruff’s Configuration Discovery
Ruff searches for configuration files in a specific order:
ruff.tomlin the current directory or parent directories.ruff.tomlin the current directory or parent directoriespyproject.tomlwith a[tool.ruff]section
I chose pyproject.toml because it keeps all my project configuration in one place. Ruff automatically finds and uses this file.
Basic Configuration: Line Length
My first attempt was simple - I just wanted to change the line length from the default 88 to 79 characters:
[tool.ruff]line-length = 79This worked immediately. Ruff now enforces 79 character lines for both formatting and the line-too-long rule (E501).
Enabling Additional Rules
Ruff has over 800 built-in rules across 50+ plugins, but only applies a core set by default. I wanted to add more rules without losing the defaults.
Here was my first mistake:
# WRONG: This disables all default rules![tool.ruff.lint]select = ["UP"] # Only pyupgrade rules enabledI used select instead of extend-select. The select option completely replaces the default rules, while extend-select adds to them. After fixing this:
# CORRECT: Extends default rules[tool.ruff.lint]extend-select = ["UP"] # Adds pyupgrade rules to defaultsNow I get both the default rules and the pyupgrade suggestions.
Adding Docstring Rules
My project requires comprehensive docstrings using Google style. I added the pydocstyle rules:
[tool.ruff.lint]extend-select = [ "UP", # pyupgrade "D", # pydocstyle]
[tool.ruff.lint.pydocstyle]convention = "google"This enables all docstring checks (the “D” prefix) and configures them to expect Google-style docstrings. Ruff now catches missing docstrings, malformed docstrings, and style inconsistencies.
Python Version-Specific Rules
Some Ruff rules only apply to specific Python versions. I needed to tell Ruff which Python version my project targets:
[project]requires-python = ">=3.10"
[tool.ruff]line-length = 79
[tool.ruff.lint]extend-select = [ "UP", # pyupgrade "D", # pydocstyle]
[tool.ruff.lint.pydocstyle]convention = "google"With requires-python = ">=3.10", Ruff applies version-specific rules correctly. For example, it won’t suggest Python 3.10+ syntax upgrades if my minimum version is 3.10.
Why This Matters for My Workflow
Configuring Ruff in pyproject.toml provides several benefits:
Team Consistency - Everyone on the team gets the same linting rules automatically. No need for each developer to configure their IDE separately.
Project-Specific Error Detection - I can enable rules that catch errors specific to my project’s needs, like comprehensive docstring checks.
Migration-Friendly - When migrating from other linters, I can configure Ruff to match existing conventions rather than reformatting everything.
Monorepo Support - Ruff supports hierarchical configuration. I can have a root pyproject.toml with shared settings and package-specific overrides in subdirectories.
Common Mistakes I Made
Using select instead of extend-select - This replaced all default rules instead of adding to them. I lost important checks like unused imports and undefined variables.
Forgetting requires-python - Without this, Ruff couldn’t apply version-specific rules correctly. It would suggest syntax that my minimum version doesn’t support.
Overlapping Formatter Rules - Some stylistic rules (like line length) overlap with Ruff’s formatter. By default, Ruff handles this gracefully, but I needed to understand which rules to keep in lint vs. formatter.
Related Knowledge: Ruff’s Rule Prefixes
Ruff uses prefixes to organize its 800+ rules:
- A: flake8-builtins (shadowing built-in names)
- ARG: flake8-unused-arguments
- B: flake8-bugbear (common bugs)
- C4: flake8-comprehensions
- D: pydocstyle (docstring conventions)
- E: pycodestyle errors
- F: pyflakes
- I: isort (import sorting)
- N: pep8-naming
- UP: pyupgrade (modernize syntax)
- W: pycodestyle warnings
Each prefix represents a plugin or rule category. You can enable entire categories or individual rules like E501 for line-too-long.
The Complete Configuration
Here’s the final pyproject.toml configuration I use:
[project]requires-python = ">=3.10"
[tool.ruff]line-length = 79
[tool.ruff.lint]extend-select = [ "UP", # pyupgrade - modernize syntax "D", # pydocstyle - docstring conventions "I", # isort - import sorting "B", # flake8-bugbear - common bugs]
[tool.ruff.lint.pydocstyle]convention = "google"
[tool.ruff.lint.isort]known-first-party = ["myproject"]This gives me line length at 79 characters, default rules plus pyupgrade, docstring checks, import sorting, and bugbear rules, all with Google-style docstrings.
Alternative: Separate Configuration File
If you prefer keeping Ruff configuration separate from pyproject.toml, you can create a ruff.toml file:
line-length = 79
[lint]extend-select = ["UP", "D", "I", "B"]
[lint.pydocstyle]convention = "google"The syntax is the same, just without the [tool.ruff] prefix. Ruff will use whichever configuration file it finds first.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments