astral-sh/ruff
ruff_python_formatter
Ruff's Python formatter. Source: crates/ruff_python_formatter/.
Purpose
Take a parsed Python file (an AST plus its trivia) and emit formatted source code. Default style targets compatibility with Black, with a small set of intentional divergences that are tracked and documented.
History
The first commit landed in February 2023. The formatter is built on a fork of Rome's rome_formatter (now crates/ruff_formatter), which provides the IR-based formatting engine. By late 2023 the formatter was stable and Black-compatible enough to be the default ruff format.
Directory layout
crates/ruff_python_formatter/
├── src/
│ ├── lib.rs
│ ├── format.rs # top-level format_module / format_expression
│ ├── statement/ # one module per statement kind
│ ├── expression/ # one module per expression kind
│ ├── pattern/ # PEP 634 patterns
│ ├── comments/ # trivia / comment placement
│ ├── prelude.rs # re-exports for rule files
│ ├── builders.rs # combinator helpers
│ ├── string/, number/ # literal formatting
│ ├── …
├── resources/test/fixtures/ # Python input files
├── tests/ # snapshot tests
└── snapshots/Key abstractions
| Type | Purpose |
|---|---|
PyFormatContext |
Carries source code, options, and comments through the format tree. |
Format<PyFormatContext> |
The trait every AST node implements. Produces formatter IR (FormatElement). |
format!, write! macros (re-exported from ruff_formatter) |
Compose IR. |
Comments |
Mapping from AST nodes to leading/trailing/dangling comments. |
How it works
graph TD
Source[Python source]
Parser[ruff_python_parser]
AST[Mod + Tokens + Trivia]
CommentMap[Comments map<br/>leading / trailing / dangling]
IR[FormatElement IR<br/>ruff_formatter]
Output[Formatted source]
Source --> Parser
Parser --> AST
AST --> CommentMap
AST --> IR
CommentMap --> IR
IR --> Output- Parse the input.
- Build a comments map that attaches each comment to a "host" AST node and a position (leading / trailing / dangling). This is the trickiest part of the formatter: where a comment ends up after formatting depends on its surrounding tree shape.
- Format the tree by walking nodes and emitting IR via the
Formattrait. Each node decides how to handle its own children, line breaks, and parenthesization. - Print the IR with a target line width, expanding or collapsing groups based on what fits.
Black compatibility
The formatter aims to produce Black-equivalent output for valid input. Differences are:
- Tracked in the formatter's
CHANGELOG.mdand the Black-divergence table. - Gated behind
preview = truewhen intentional divergence is in flight.
cargo dev format-dev runs the formatter against a curated corpus and checks that two passes produce identical output (idempotency).
Stability
Stability — format(format(x)) == format(x) — is enforced by both the dev harness and a fuzz target. Any rule that breaks idempotency is treated as a bug.
Integration points
- Consumed by
ruffCLI (ruff format),ruff_server(format on save),ruff_wasm. - Depends on
ruff_python_parser,ruff_python_ast,ruff_python_trivia,ruff_formatter(the engine). - Snapshot fixtures shared with the upstream Black project where possible.
Modifying the formatter
Most changes go in
expression/orstatement/<construct>.rs. Pick the file whose name matches the AST node kind.Comment placement issues usually live in
comments/.After any change, run:
cargo nextest run -p ruff_python_formatter cargo dev format-dev --stability-checkSnapshot deltas should be small. Big snapshot churn usually means an unintended ripple — review carefully before committing.
See features/formatter for the user-facing perspective.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.