astral-sh/ruff
Formatter
End-to-end view of ruff format.
What the user sees
ruff format src/ # rewrite files in place
ruff format --check src/ # exit non-zero if anything needs reformatting
ruff format --diff src/ # show what would changeThe formatter aims to produce Black-compatible output by default, with a small list of intentional divergences documented in the formatter's CHANGELOG.md.
Pipeline
graph TD
User[user runs ruff format]
CLI[ruff format command<br/>crates/ruff/src/commands/format.rs]
Workspace[ruff_workspace<br/>resolve format settings]
Walker[file discovery]
Files[Python files]
Parser[ruff_python_parser]
AST[Mod AST + tokens + trivia]
Comments[Comments map<br/>leading / trailing / dangling]
FormatTree[Format trait dispatch<br/>crates/ruff_python_formatter]
IR[FormatElement IR<br/>crates/ruff_formatter]
Printer[Printer with target line width]
Output[Formatted source]
User --> CLI
CLI --> Workspace
CLI --> Walker
Walker --> Files
Files --> Parser
Parser --> AST
AST --> Comments
AST --> FormatTree
Comments --> FormatTree
FormatTree --> IR
IR --> Printer
Printer --> OutputWhat's handled where
| Stage | Where |
|---|---|
| Tokenization + parsing | ruff_python_parser |
| Comment placement | crates/ruff_python_formatter/src/comments/ — attaches each comment to a host node and a slot (leading / trailing / dangling) |
| Per-node formatting | crates/ruff_python_formatter/src/{statement,expression,pattern}/ — one module per AST kind |
| IR engine | crates/ruff_formatter — fork of rome_formatter with Format, format!, write!, groups, soft lines |
| Printing | crates/ruff_formatter — width-aware printer that decides which groups break |
Configuration knobs
ruff_workspace exposes [tool.ruff.format] settings:
line-length(defaults to 88, matching Black)indent-width(defaults to 4)quote-style(single,double,preserve)indent-style(space,tab)skip-magic-trailing-comma(mirrors Black's behavior)line-ending(auto,lf,crlf,cr,native)docstring-code-format,docstring-code-line-length(format code blocks within docstrings)
These are layered onto the PyFormatOptions consumed by the formatter.
Stability and idempotency
format(format(x)) == format(x) is required. Two harnesses enforce it:
cargo dev format-dev --stability-checkruns the formatter against the curated corpus and asserts idempotency.- A fuzz target generates random valid Python and checks the same property.
A single non-idempotent rule is a release blocker.
Black compatibility
When a fixture diverges from Black's output, it's tracked in:
- The formatter's
CHANGELOG.md(intentional differences) - The Black-compatibility status table maintained in the docs
- A snapshot test that pins the divergence
Promoting from "preview-only" to "stable" requires consensus from the formatter team.
Notebook support
Jupyter notebooks (.ipynb) are formatted cell-by-cell via ruff_notebook. Cell types other than code (markdown, raw) are passed through; code cells go through the same formatter pipeline.
Where to start when extending
- A specific syntax doesn't format right: find the corresponding module under
expression/orstatement/. The file names mirror the AST node kinds. - Comment placement bug:
crates/ruff_python_formatter/src/comments/. - New configuration option: add it in
crates/ruff_python_formatter/src/options.rsand propagate intoruff_workspace. - Performance:
cargo bench -p ruff_benchmarkexercises both lint and format paths.
For the crate-level view, see ruff_python_formatter.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.