python-ruff
Python Ruff — Linting and Formatting
Ruff is an extremely fast Python linter and code formatter written in Rust. It replaces Flake8, Black, isort, pydocstyle, pyupgrade, and autoflake with a single unified tool that runs 10–100x faster than any of them individually. Ruff supports over 800 built-in rules and provides automatic fix capabilities for many violations.
Installation
Install Ruff via pip, uv, or as a development dependency:
pip install ruff
uv add --dev ruff
Core Commands
ruff check # Lint all files in current directory
ruff check --fix # Lint and auto-fix safe violations
ruff check --unsafe-fixes # Show unsafe fixes (review before applying)
ruff check --fix --unsafe-fixes # Apply all fixes including unsafe ones
ruff check --watch # Re-lint on file changes
ruff format # Format all files in current directory
ruff format --check # Check formatting without writing changes
ruff format --diff # Show formatting diff without writing
Run both linting (with import sorting) and formatting in sequence:
ruff check --select I --fix # Sort imports first
ruff format # Then format
Configuration
Ruff reads from pyproject.toml, ruff.toml, or .ruff.toml. All three support the same schema; ruff.toml and .ruff.toml omit the [tool.ruff] prefix.
Recommended Starter Configuration
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # Pyflakes
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"I", # isort
]
ignore = ["E501"] # line-too-long (handled by formatter)
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true
See references/configuration-guide.md for the full configuration reference.
Rule Selection Best Practices
Rule codes follow the pattern PREFIX + digits (e.g., F401 = Pyflakes unused import, E711 = pycodestyle comparison to None).
Start small, then expand:
# Step 1: Minimal (safe baseline)
select = ["E4", "E7", "E9", "F"]
# Step 2: Recommended expansion
select = ["E", "F", "UP", "B", "SIM", "I"]
# Step 3: Strict (with targeted ignores)
select = ["ALL"]
ignore = ["D", "ANN", "COM812", "ISC001"]
Key rule prefixes:
| Prefix | Source | Purpose |
|---|---|---|
E/W |
pycodestyle | Style errors and warnings |
F |
Pyflakes | Logical errors, unused imports |
B |
flake8-bugbear | Likely bugs and design issues |
UP |
pyupgrade | Upgrade to modern Python syntax |
SIM |
flake8-simplify | Code simplification |
I |
isort | Import sorting |
N |
pep8-naming | Naming convention checks |
D |
pydocstyle | Docstring conventions |
ANN |
flake8-annotations | Type annotation enforcement |
S |
flake8-bandit | Security checks |
RUF |
Ruff-native | Ruff-specific rules |
Use lint.select (not lint.extend-select) to make rule sets explicit. Avoid enabling ALL without carefully curating an ignore list, as it enables new rules on every Ruff upgrade.
See references/rule-categories.md for detailed rule guidance and common ignores.
Automatic Fixes
Ruff distinguishes between safe and unsafe fixes:
- Safe fixes preserve code behavior exactly — applied by default with
--fix - Unsafe fixes may change runtime behavior (e.g., exception types, removed comments) — opt-in with
--unsafe-fixes
Promote or demote fix safety per rule:
[tool.ruff.lint]
extend-safe-fixes = ["UP"] # Treat pyupgrade fixes as safe
extend-unsafe-fixes = ["B"] # Require explicit opt-in for bugbear fixes
Disable auto-fix for specific rules while keeping them as violations:
[tool.ruff.lint]
fixable = ["ALL"]
unfixable = ["F401"] # Flag unused imports but don't auto-remove them
Error Suppression
Line-level suppression
import os # noqa: F401
x = 1 # noqa: E741, F841 # suppress multiple rules
y = 1 # noqa # suppress all (avoid — too broad)
Block-level suppression (preferred over blanket noqa)
# ruff: disable[E501]
LONG_CONSTANT_1 = "Lorem ipsum dolor sit amet, consectetur adipiscing..."
LONG_CONSTANT_2 = "Lorem ipsum dolor sit amet, consectetur adipiscing..."
# ruff: enable[E501]
File-level suppression
# ruff: noqa: F841 # suppress specific rule for entire file
# ruff: noqa # suppress all (avoid — use per-file-ignores instead)
Per-file ignores in config (preferred)
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # re-exports are intentional
"tests/**/*.py" = ["S101", "ANN"] # asserts and no annotations in tests
"scripts/**/*.py" = ["T20"] # print() allowed in scripts
Detecting unused suppressions
ruff check --extend-select RUF100 # flag stale noqa comments
ruff check --extend-select RUF100 --fix # auto-remove stale noqa
Formatter Configuration
The Ruff formatter is a drop-in replacement for Black. Key options:
[tool.ruff.format]
quote-style = "double" # "double" | "single" | "preserve"
indent-style = "space" # "space" | "tab"
line-ending = "auto" # "auto" | "lf" | "crlf" | "native"
skip-magic-trailing-comma = false # respect trailing commas (like Black)
docstring-code-format = true # format code examples in docstrings
Suppressing formatter
# fmt: off
matrix = [1,0,0,
0,1,0,
0,0,1]
# fmt: on
result = some_call() # fmt: skip
Rules incompatible with the formatter
Disable these rules to avoid conflicts when using ruff format:
[tool.ruff.lint]
ignore = [
"W191", # tab-indentation
"E111", # indentation-with-invalid-multiple
"COM812", # missing-trailing-comma
"ISC002", # multi-line-implicit-string-concatenation
"Q000", # bad-quotes-inline-string
"Q001", # bad-quotes-multiline-string
"Q002", # bad-quotes-docstring
]
CI and Pre-commit Integration
GitHub Actions
- name: Lint and format check
run: |
ruff check .
ruff format --check .
pre-commit hooks
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
Migrating an existing codebase
Auto-add noqa directives to all current violations, then clean up incrementally:
ruff check --add-noqa . # adds noqa to all failing lines
ruff check --extend-select RUF100 --fix . # remove stale noqa over time
Quick Reference
| Task | Command |
|---|---|
| Lint current directory | ruff check |
| Lint and fix safe violations | ruff check --fix |
| Format current directory | ruff format |
| Check formatting (CI mode) | ruff format --check |
| Sort imports only | ruff check --select I --fix |
| Explain a rule | ruff rule F401 |
| List all rules | ruff linter |
| Show active config for a file | ruff check --show-settings <file.py> |
| Flag unused noqa comments | ruff check --extend-select RUF100 |
Additional Resources
Reference Files
references/rule-categories.md— Detailed rule prefixes, common ignores, and per-category guidancereferences/configuration-guide.md— Full configuration options for linter and formatter
Example Files
examples/pyproject.toml— Production-ready pyproject.toml configurationexamples/ruff.toml— Standalone ruff.toml configuration (monorepo root)examples/pre-commit-config.yaml— pre-commit hooks configuration
More from the-perfect-developer/the-perfect-opencode
html
Apply Google HTML style guide conventions to HTML code
19turso-libsql
This skill should be used when the user asks to "connect to Turso", "use libSQL", "set up a Turso database", "query Turso with TypeScript", or needs guidance on Turso Cloud, embedded replicas, or vector search with libSQL.
11alpinejs
This skill should be used when the user asks to "add Alpine.js", "create Alpine component", "use Alpine directives", "build interactive UI with Alpine", or needs guidance on Alpine.js development patterns and best practices.
10python-dependency-injection
This skill should be used when the user asks to "implement dependency injection in Python", "use the dependency-injector library", "decouple Python components", "write testable Python services", or needs guidance on Inversion of Control, DI containers, provider types, and wiring in Python applications.
3copilot-sdk
This skill should be used when the user asks to "integrate GitHub Copilot into an app", "use the Copilot SDK", "build a Copilot-powered agent", "embed Copilot in a service", or needs guidance on the GitHub Copilot SDK for Python, TypeScript, Go, or .NET.
3conventional-git-commit
This skill MUST be loaded on every git commit without exception. It should also be used when the user asks to "write a conventional commit", "format a commit message", "follow conventional commits spec", "create a semantic commit", "make a commit", "commit changes", or "git commit". Every commit message produced in this project MUST conform to this specification.
3