Rework the documentation to incorporate the Ruff formatter (#7732)

## Summary

This PR updates our documentation for the upcoming formatter release.

Broadly, the documentation is now structured as follows:

- Overview
- Tutorial
- Installing Ruff
- The Ruff Linter
    - Overview
    - `ruff check`
    - Rule selection
    - Error suppression
    - Exit codes
- The Ruff Formatter
    - Overview
    - `ruff format`
    - Philosophy
    - Configuration
    - Format suppression
    - Exit codes
    - Black compatibility
        - Known deviations
- Configuring Ruff
    - pyproject.toml
    - File discovery
    - Configuration discovery
    - CLI
    - Shell autocompletion
- Preview
- Rules
- Settings
- Integrations
    - `pre-commit`
    - VS Code
    - LSP
    - PyCharm
    - GitHub Actions
- FAQ
- Contributing

The major changes include:

- Removing the "Usage" section from the docs, and instead folding that
information into "Integrations" and the new Linter and Formatter
sections.
- Breaking up "Configuration" into "Configuring Ruff" (for generic
configuration), and new Linter- and Formatter-specific sections.
- Updating all example configurations to use `[tool.ruff.lint]` and
`[tool.ruff.format]`.

My suggestion is to pull and build the docs locally, and review by
reading them in the browser rather than trying to parse all the code
changes.

Closes https://github.com/astral-sh/ruff/issues/7235.

Closes https://github.com/astral-sh/ruff/issues/7647.
This commit is contained in:
Charlie Marsh 2023-10-20 19:08:26 -04:00 committed by GitHub
parent fa556d1c74
commit f6d6200aae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1593 additions and 572 deletions

View file

@ -24,6 +24,10 @@ repos:
additional_dependencies:
- mdformat-mkdocs
- mdformat-admon
exclude: |
(?x)^(
docs/formatter/black.md
)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.33.0

102
README.md
View file

@ -10,7 +10,7 @@
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://docs.astral.sh/ruff/) | [**Playground**](https://play.ruff.rs/)
An extremely fast Python linter, written in Rust.
An extremely fast Python linter and code formatter, written in Rust.
<p align="center">
<picture align="center">
@ -24,17 +24,16 @@ An extremely fast Python linter, written in Rust.
<i>Linting the CPython codebase from scratch.</i>
</p>
- ⚡️ 10-100x faster than existing linters
- ⚡️ 10-100x faster than existing linters (like Flake8) and formatters (like Black)
- 🐍 Installable via `pip`
- 🛠️ `pyproject.toml` support
- 🤝 Python 3.12 compatibility
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8), isort, and Black
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
- 📏 Over [700 built-in rules](https://docs.astral.sh/ruff/rules/)
- ⚖️ [Near-parity](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8) with the
built-in Flake8 rule set
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/editor-integrations/) for
- 📏 Over [700 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
of popular Flake8 plugins, like flake8-bugbear
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/integrations/) for
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://docs.astral.sh/ruff/configuration/#pyprojecttoml-discovery)
@ -42,10 +41,10 @@ Ruff aims to be orders of magnitude faster than alternative tools while integrat
functionality behind a single, common interface.
Ruff can be used to replace [Flake8](https://pypi.org/project/flake8/) (plus dozens of plugins),
[isort](https://pypi.org/project/isort/), [pydocstyle](https://pypi.org/project/pydocstyle/),
[yesqa](https://github.com/asottile/yesqa), [eradicate](https://pypi.org/project/eradicate/),
[pyupgrade](https://pypi.org/project/pyupgrade/), and [autoflake](https://pypi.org/project/autoflake/),
all while executing tens or hundreds of times faster than any individual tool.
[Black](https://github.com/psf/black), [isort](https://pypi.org/project/isort/),
[pydocstyle](https://pypi.org/project/pydocstyle/), [pyupgrade](https://pypi.org/project/pyupgrade/),
[autoflake](https://pypi.org/project/autoflake/), and more, all while executing tens or hundreds of
times faster than any individual tool.
Ruff is extremely actively developed and used in major open-source projects like:
@ -126,23 +125,41 @@ and with [a variety of other package managers](https://docs.astral.sh/ruff/insta
### Usage
To run Ruff, try any of the following:
To run Ruff as a linter, try any of the following:
```shell
ruff check . # Lint all files in the current directory (and any subdirectories)
ruff check path/to/code/ # Lint all files in `/path/to/code` (and any subdirectories)
ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code`
ruff check path/to/code/to/file.py # Lint `file.py`
ruff check . # Lint all files in the current directory (and any subdirectories).
ruff check path/to/code/ # Lint all files in `/path/to/code` (and any subdirectories).
ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code`.
ruff check path/to/code/to/file.py # Lint `file.py`.
ruff check @arguments.txt # Lint using an input file, treating its contents as newline-delimited command-line arguments.
```
Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
Or, to run Ruff as a formatter:
```shell
ruff format . # Format all files in the current directory (and any subdirectories).
ruff format path/to/code/ # Format all files in `/path/to/code` (and any subdirectories).
ruff format path/to/code/*.py # Format all `.py` files in `/path/to/code`.
ruff format path/to/code/to/file.py # Format `file.py`.
ruff format @arguments.txt # Format using an input file, treating its contents as newline-delimited command-line arguments.
```
Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit):
```yaml
# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.1
hooks:
- id: ruff
# Run the Ruff formatter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.291
hooks:
- id: ruff-format
```
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or
@ -168,18 +185,10 @@ Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml`
[_Configuration_](https://docs.astral.sh/ruff/configuration/), or [_Settings_](https://docs.astral.sh/ruff/settings/)
for a complete list of all configuration options).
If left unspecified, the default configuration is equivalent to:
If left unspecified, Ruff's default configuration is equivalent to:
```toml
[tool.ruff]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
@ -207,27 +216,46 @@ exclude = [
# Same as Black.
line-length = 88
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
indent-width = 4
# Assume Python 3.8
target-version = "py38"
[tool.ruff.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
magic-trailing-comma = "respect"
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
```
Some configuration options can be provided via the command-line, such as those related to
rule enablement and disablement, file discovery, logging level, and more:
rule enablement and disablement, file discovery, and logging level:
```shell
ruff check path/to/code/ --select F401 --select F403 --quiet
```
See `ruff help` for more on Ruff's top-level commands, or `ruff help check` for more on the
linting command.
See `ruff help` for more on Ruff's top-level commands, or `ruff help check` and `ruff help format`
for more on the linting and formatting commands, respectively.
## Rules
@ -238,7 +266,7 @@ isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implement
Rust as a first-party feature.
By default, Ruff enables Flake8's `F` rules, along with a subset of the `E` rules, omitting any
stylistic rules that overlap with the use of a formatter, like
stylistic rules that overlap with the use of a formatter, like `ruff format` or
[Black](https://github.com/psf/black).
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
@ -330,7 +358,7 @@ In some cases, Ruff includes a "direct" Rust port of the corresponding tool.
We're grateful to the maintainers of these tools for their work, and for all
the value they've provided to the Python community.
Ruff's autoformatter is built on a fork of Rome's [`rome_formatter`](https://github.com/rome/tools/tree/main/crates/rome_formatter),
Ruff's formatter is built on a fork of Rome's [`rome_formatter`](https://github.com/rome/tools/tree/main/crates/rome_formatter),
and again draws on both API and implementation details from [Rome](https://github.com/rome/tools),
[Prettier](https://github.com/prettier/prettier), and [Black](https://github.com/psf/black).

View file

@ -16,8 +16,11 @@ use crate::ROOT_DIR;
const COMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated command help. -->\n";
const COMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated command help. -->";
const SUBCOMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated subcommand help. -->\n";
const SUBCOMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated subcommand help. -->";
const CHECK_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated check help. -->\n";
const CHECK_HELP_END_PRAGMA: &str = "<!-- End auto-generated check help. -->";
const FORMAT_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated format help. -->\n";
const FORMAT_HELP_END_PRAGMA: &str = "<!-- End auto-generated format help. -->";
#[derive(clap::Args)]
pub(crate) struct Args {
@ -56,11 +59,15 @@ pub(super) fn main(args: &Args) -> Result<()> {
let command_help = trim_lines(&help_text());
// Generate `ruff help check`.
let subcommand_help = trim_lines(&check_help_text());
let check_help = trim_lines(&subcommand_help_text("check")?);
// Generate `ruff help format`.
let format_help = trim_lines(&subcommand_help_text("format")?);
if args.mode.is_dry_run() {
print!("{command_help}");
print!("{subcommand_help}");
print!("{check_help}");
print!("{format_help}");
return Ok(());
}
@ -77,9 +84,15 @@ pub(super) fn main(args: &Args) -> Result<()> {
)?;
let new = replace_docs_section(
&new,
&format!("```text\n{subcommand_help}\n```\n\n"),
SUBCOMMAND_HELP_BEGIN_PRAGMA,
SUBCOMMAND_HELP_END_PRAGMA,
&format!("```text\n{check_help}\n```\n\n"),
CHECK_HELP_BEGIN_PRAGMA,
CHECK_HELP_END_PRAGMA,
)?;
let new = replace_docs_section(
&new,
&format!("```text\n{format_help}\n```\n\n"),
FORMAT_HELP_BEGIN_PRAGMA,
FORMAT_HELP_END_PRAGMA,
)?;
match args.mode {
@ -104,18 +117,19 @@ fn help_text() -> String {
args::Args::command().render_help().to_string()
}
/// Returns the output of `ruff help check`.
fn check_help_text() -> String {
/// Returns the output of a given subcommand (e.g., `ruff help check`).
fn subcommand_help_text(subcommand: &str) -> Result<String> {
let mut cmd = args::Args::command();
// The build call is necessary for the help output to contain `Usage: ruff
// check` instead of `Usage: check` see https://github.com/clap-rs/clap/issues/4685
cmd.build();
cmd.find_subcommand_mut("check")
.expect("`check` subcommand not found")
Ok(cmd
.find_subcommand_mut(subcommand)
.with_context(|| format!("Unable to find subcommand `{subcommand}`"))?
.render_help()
.to_string()
.to_string())
}
#[cfg(test)]

View file

@ -20,7 +20,7 @@ use crate::settings::LinterSettings;
/// negatively affects code readability.
///
/// In some cases, the implicit concatenation may also be unintentional, as
/// autoformatters are capable of introducing single-line implicit
/// code formatters are capable of introducing single-line implicit
/// concatenations when collapsing long lines.
///
/// ## Example

View file

@ -176,7 +176,7 @@ original intent, at the cost of retaining additional vertical space.
This deviation only impacts unformatted code, in that Ruff's output should not deviate for code that
has already been formatted by Black.
### Pragma comments are ignored when computing line width
#### Pragma comments are ignored when computing line width
Pragma comments (`# type`, `# noqa`, `# pyright`, `# pylint`, etc.) are ignored when computing the width of a line.
This prevents Ruff from moving pragma comments around, thereby modifying their meaning and behavior:
@ -209,14 +209,14 @@ on both `first()` and `second()`:
]
```
### Line width vs. line length
#### Line width vs. line length
Ruff uses the Unicode width of a line to determine if a line fits. Black's stable style uses
character width, while Black's preview style uses Unicode width for strings ([#3445](https://github.com/psf/black/pull/3445)),
and character width for all other tokens. Ruff's behavior is closer to Black's preview style than
Black's stable style, although Ruff _also_ uses Unicode width for identifiers and comments.
### Walruses in slice expressions
#### Walruses in slice expressions
Black avoids inserting space around `:=` operators within slices. For example, the following adheres
to Black stable style:
@ -241,7 +241,7 @@ x[y := 1]
This will likely be incorporated into Black's preview style ([#3823](https://github.com/psf/black/pull/3823)).
### `global` and `nonlocal` names are broken across multiple lines by continuations
#### `global` and `nonlocal` names are broken across multiple lines by continuations
If a `global` or `nonlocal` statement includes multiple names, and exceeds the configured line
width, Ruff will break them across multiple lines using continuations:
@ -259,7 +259,7 @@ global \
analyze_size_model
```
### Newlines are inserted after all class docstrings
#### Newlines are inserted after all class docstrings
Black typically enforces a single newline after a class docstring. However, it does not apply such
formatting if the docstring is single-quoted rather than triple-quoted, while Ruff enforces a
@ -289,7 +289,7 @@ class IntFromGeom(GEOSFuncFactory):
errcheck = staticmethod(check_minus_one)
```
### Trailing own-line comments on imports are not moved to the next line
#### Trailing own-line comments on imports are not moved to the next line
Black enforces a single empty line between an import and a trailing own-line comment. Ruff leaves
such comments in-place:
@ -315,7 +315,7 @@ import os
import sys
```
### Parentheses around awaited collections are not preserved
#### Parentheses around awaited collections are not preserved
Black preserves parentheses around awaited collections:
@ -333,7 +333,7 @@ This is more consistent to the formatting of other awaited expressions: Ruff and
remove parentheses around, e.g., `await (1)`, only retaining them when syntactically required,
as in, e.g., `await (x := 1)`.
### Implicit string concatenations in attribute accesses ([#7052](https://github.com/astral-sh/ruff/issues/7052))
#### Implicit string concatenations in attribute accesses ([#7052](https://github.com/astral-sh/ruff/issues/7052))
Given the following unformatted code:
@ -375,7 +375,7 @@ In general, Black splits implicit string concatenations over multiple lines more
even if those concatenations _can_ fit on a single line. Ruff instead avoids splitting such
concatenations unless doing so is necessary to fit within the configured line width.
### Own-line comments on expressions don't cause the expression to expand ([#7314](https://github.com/astral-sh/ruff/issues/7314))
#### Own-line comments on expressions don't cause the expression to expand ([#7314](https://github.com/astral-sh/ruff/issues/7314))
Given an expression like:
@ -406,7 +406,7 @@ initial formatting:
)
```
### Tuples are parenthesized when expanded ([#7317](https://github.com/astral-sh/ruff/issues/7317))
#### Tuples are parenthesized when expanded ([#7317](https://github.com/astral-sh/ruff/issues/7317))
Ruff tends towards parenthesizing tuples (with a few exceptions), while Black tends to remove tuple
parentheses more often.
@ -454,7 +454,7 @@ for a, f(
pass
```
### Single-element tuples are always parenthesized
#### Single-element tuples are always parenthesized
Ruff always inserts parentheses around single-element tuples, while Black will omit them in some
cases:
@ -473,7 +473,7 @@ cases:
Adding parentheses around single-element tuples adds visual distinction and helps avoid "accidental"
tuples created by extraneous trailing commas (see, e.g., [#17181](https://github.com/django/django/pull/17181)).
### Trailing commas are inserted when expanding a function definition with a single argument ([#7323](https://github.com/astral-sh/ruff/issues/7323))
#### Trailing commas are inserted when expanding a function definition with a single argument ([#7323](https://github.com/astral-sh/ruff/issues/7323))
When a function definition with a single argument is expanded over multiple lines, Black
will add a trailing comma in some cases, depending on whether the argument includes a type
@ -506,7 +506,7 @@ def func(
Ruff will instead insert a trailing comma in all such cases for consistency.
### Parentheses around call-chain assignment values are not preserved ([#7320](https://github.com/astral-sh/ruff/issues/7320))
#### Parentheses around call-chain assignment values are not preserved ([#7320](https://github.com/astral-sh/ruff/issues/7320))
Given:
@ -539,7 +539,7 @@ def update_emission_strength():
value = self.emission_strength * 2
```
### Type annotations may be parenthesized when expanded ([#7315](https://github.com/astral-sh/ruff/issues/7315))
#### Type annotations may be parenthesized when expanded ([#7315](https://github.com/astral-sh/ruff/issues/7315))
Black will avoid parenthesizing type annotations in an annotated assignment, while Ruff will insert
parentheses in some cases.
@ -561,7 +561,7 @@ StartElementHandler: (
)
```
### Call chain calls break differently ([#7051](https://github.com/astral-sh/ruff/issues/7051))
#### Call chain calls break differently ([#7051](https://github.com/astral-sh/ruff/issues/7051))
Black occasionally breaks call chains differently than Ruff; in particular, Black occasionally
expands the arguments for the last call in the chain, as in:
@ -630,7 +630,7 @@ df.drop(columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]).drop_duplicates(a).rename
).to_csv(path / "aaaaaa.csv", index=False).other(a)
```
### Expressions with (non-pragma) trailing comments are split more often ([#7823](https://github.com/astral-sh/ruff/issues/7823))
#### Expressions with (non-pragma) trailing comments are split more often ([#7823](https://github.com/astral-sh/ruff/issues/7823))
Both Ruff and Black will break the following expression over multiple lines, since it then allows
the expression to fit within the configured line width:

View file

@ -2,8 +2,10 @@
Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file.
For a complete enumeration of the available configuration options, see
[_Settings_](settings.md).
Whether you're using Ruff as a linter, formatter, or both, the underlying configuration strategy and
semantics are the same.
For a complete enumeration of the available configuration options, see [_Settings_](settings.md).
## Using `pyproject.toml`
@ -11,16 +13,6 @@ If left unspecified, Ruff's default configuration is equivalent to:
```toml
[tool.ruff]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
@ -45,22 +37,46 @@ exclude = [
"node_modules",
"venv",
]
per-file-ignores = {}
# Same as Black.
line-length = 88
indent-width = 4
# Assume Python 3.8
target-version = "py38"
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F"]
ignore = []
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
# Assume Python 3.8
target-version = "py38"
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
magic-trailing-comma = "respect"
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
```
As an example, the following would configure Ruff to:
```toml
[tool.ruff]
[tool.ruff.lint]
# 1. Enable flake8-bugbear (`B`) rules, in addition to the defaults.
select = ["E", "F", "B"]
@ -70,45 +86,28 @@ ignore = ["E501"]
# 3. Avoid trying to fix flake8-bugbear (`B`) violations.
unfixable = ["B"]
# 4. Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
[tool.ruff.per-file-ignores]
# 4. Ignore `E402` (import violations) in all `__init__.py` files, and in select subdirectories.
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["E402"]
"path/to/file.py" = ["E402"]
"**/{tests,docs,tools}/*" = ["E402"]
[tool.ruff.format]
# 5. Use single quotes for non-triple-quoted strings.
quote-style = "single"
```
Plugin configurations should be expressed as subsections, e.g.:
Linter plugin configurations are expressed as subsections, e.g.:
```toml
[tool.ruff]
[tool.ruff.lint]
# Add "Q" to the list of enabled codes.
select = ["E", "F", "Q"]
[tool.ruff.flake8-quotes]
[tool.ruff.lint.flake8-quotes]
docstring-quotes = "double"
```
For a complete enumeration of the available configuration options, see
[_Settings_](settings.md).
Ruff mirrors Flake8's rule code system, in which each rule code consists of a one-to-three letter
prefix, followed by three digits (e.g., `F401`). The prefix indicates that "source" of the rule
(e.g., `F` for Pyflakes, `E` for pycodestyle, `ANN` for flake8-annotations). The set of enabled
rules is determined by the `select` and `ignore` options, which accept either the full code (e.g.,
`F401`) or the prefix (e.g., `F`).
As a special-case, Ruff also supports the `ALL` code, which enables all rules. Note that some
pydocstyle rules conflict (e.g., `D203` and `D211`) as they represent alternative docstring
formats. Ruff will automatically disable any conflicting rules when `ALL` is enabled.
If you're wondering how to configure Ruff, here are some **recommended guidelines**:
- Prefer `select` and `ignore` over `extend-select` and `extend-ignore`, to make your rule set
explicit.
- Use `ALL` with discretion. Enabling `ALL` will implicitly enable new rules whenever you upgrade.
- Start with a small set of rules (`select = ["E", "F"]`) and add a category at-a-time. For example,
you might consider expanding to `select = ["E", "F", "B"]` to enable the popular flake8-bugbear
extension.
For a complete enumeration of the available configuration options, see [_Settings_](settings.md).
## Using `ruff.toml`
@ -120,6 +119,7 @@ For example, the `pyproject.toml` described above would be represented via the f
`ruff.toml` (or `.ruff.toml`):
```toml
[lint]
# Enable flake8-bugbear (`B`) rules.
select = ["E", "F", "B"]
@ -129,14 +129,104 @@ ignore = ["E501"]
# Avoid trying to fix flake8-bugbear (`B`) violations.
unfixable = ["B"]
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
[per-file-ignores]
# Ignore `E402` (import violations) in all `__init__.py` files, and in select subdirectories.
[lint.per-file-ignores]
"__init__.py" = ["E402"]
"path/to/file.py" = ["E402"]
"**/{tests,docs,tools}/*" = ["E402"]
[format]
# Use single quotes for non-triple-quoted strings.
quote-style = "single"
```
For a complete enumeration of the available configuration options, see [_Settings_](settings.md).
## `pyproject.toml` discovery
Similar to [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy),
Ruff supports hierarchical configuration, such that the "closest" `pyproject.toml` file in the
directory hierarchy is used for every individual file, with all paths in the `pyproject.toml` file
(e.g., `exclude` globs, `src` paths) being resolved relative to the directory containing that
`pyproject.toml` file.
There are a few exceptions to these rules:
1. In locating the "closest" `pyproject.toml` file for a given path, Ruff ignores any
`pyproject.toml` files that lack a `[tool.ruff]` section.
1. If a configuration file is passed directly via `--config`, those settings are used for _all_
analyzed files, and any relative paths in that configuration file (like `exclude` globs or
`src` paths) are resolved relative to the _current_ working directory.
1. If no `pyproject.toml` file is found in the filesystem hierarchy, Ruff will fall back to using
a default configuration. If a user-specific configuration file exists
at `${config_dir}/ruff/pyproject.toml`, that file will be used instead of the default
configuration, with `${config_dir}` being determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html)
crate, and all relative paths being again resolved relative to the _current working directory_.
1. Any `pyproject.toml`-supported settings that are provided on the command-line (e.g., via
`--select`) will override the settings in _every_ resolved configuration file.
Unlike [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy),
Ruff does not merge settings across configuration files; instead, the "closest" configuration file
is used, and any parent configuration files are ignored. In lieu of this implicit cascade, Ruff
supports an [`extend`](settings.md#extend) field, which allows you to inherit the settings from another
`pyproject.toml` file, like so:
```toml
[tool.ruff]
# Extend the `pyproject.toml` file in the parent directory...
extend = "../pyproject.toml"
# ...but use a different line length.
line-length = 100
```
All of the above rules apply equivalently to `ruff.toml` and `.ruff.toml` files. If Ruff detects
multiple configuration files in the same directory, the `.ruff.toml` file will take precedence over
the `ruff.toml` file, and the `ruff.toml` file will take precedence over the `pyproject.toml` file.
## Python file discovery
When passed a path on the command-line, Ruff will automatically discover all Python files in that
path, taking into account the [`exclude`](settings.md#exclude) and [`extend-exclude`](settings.md#extend-exclude)
settings in each directory's `pyproject.toml` file.
Files can also be selectively excluded from linting or formatting by scoping the `exclude` and
`extend-exclude` settings to the tool-specific configuration tables. For example, the following
would prevent `ruff` from formatting `.pyi` files, but would continue to include them in linting:
```toml
[tool.ruff.format]
extend-exclude = ["*.pyi"]
```
By default, Ruff will also skip any files that are omitted via `.ignore`, `.gitignore`,
`.git/info/exclude`, and global `gitignore` files (see: [`respect-gitignore`](settings.md#respect-gitignore)).
Files that are passed to `ruff` directly are always analyzed, regardless of the above criteria.
For example, `ruff check /path/to/excluded/file.py` will always lint `file.py`.
## Jupyter Notebook discovery
Ruff has built-in support for [Jupyter Notebooks](https://jupyter.org/).
To opt in to linting and formatting Jupyter Notebook (`.ipynb`) files, add the `*.ipynb` pattern to
your [`extend-include`](settings.md#extend-include) setting, like so:
```toml
[tool.ruff]
extend-include = ["*.ipynb"]
```
This will prompt Ruff to discover Jupyter Notebook (`.ipynb`) files in any specified
directories, then lint and format them accordingly.
Alternatively, pass the notebook file(s) to `ruff` on the command-line directly. For example,
`ruff check /path/to/notebook.ipynb` will always lint `notebook.ipynb`. Similarly,
`ruff format /path/to/notebook.ipynb` will always format `notebook.ipynb`.
All of the above rules apply equivalently to `ruff.toml` and `.ruff.toml` files. If Ruff detects
multiple configuration files in the same directory, the `.ruff.toml` file will take precedence over
the `ruff.toml` file, and the `ruff.toml` file will take precedence over the `pyproject.toml` file.
## Command-line interface
Some configuration options can be provided via the command-line, such as those related to rule
@ -180,7 +270,7 @@ For help with a specific command, see: `ruff help <command>`.
Or `ruff help check` for more on the linting command:
<!-- Begin auto-generated subcommand help. -->
<!-- Begin auto-generated check help. -->
```text
Run Ruff on the given files or directories (default)
@ -272,304 +362,50 @@ Log levels:
-s, --silent Disable all logging (but still exit with status code "1" upon detecting diagnostics)
```
<!-- End auto-generated subcommand help. -->
<!-- End auto-generated check help. -->
## `pyproject.toml` discovery
Or `ruff help format` for more on the formatting command:
Similar to [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy),
Ruff supports hierarchical configuration, such that the "closest" `pyproject.toml` file in the
directory hierarchy is used for every individual file, with all paths in the `pyproject.toml` file
(e.g., `exclude` globs, `src` paths) being resolved relative to the directory containing that
`pyproject.toml` file.
<!-- Begin auto-generated format help. -->
There are a few exceptions to these rules:
```text
Run the Ruff formatter on the given files or directories
1. In locating the "closest" `pyproject.toml` file for a given path, Ruff ignores any
`pyproject.toml` files that lack a `[tool.ruff]` section.
1. If a configuration file is passed directly via `--config`, those settings are used for across
files. Any relative paths in that configuration file (like `exclude` globs or `src` paths) are
resolved relative to the _current working directory_.
1. If no `pyproject.toml` file is found in the filesystem hierarchy, Ruff will fall back to using
a default configuration. If a user-specific configuration file exists
at `${config_dir}/ruff/pyproject.toml`, that file will be used instead of the default
configuration, with `${config_dir}` being determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html)
crate, and all relative paths being again resolved relative to the _current working directory_.
1. Any `pyproject.toml`-supported settings that are provided on the command-line (e.g., via
`--select`) will override the settings in _every_ resolved configuration file.
Usage: ruff format [OPTIONS] [FILES]...
Unlike [ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#cascading-and-hierarchy),
Ruff does not merge settings across configuration files; instead, the "closest" configuration file
is used, and any parent configuration files are ignored. In lieu of this implicit cascade, Ruff
supports an [`extend`](settings.md#extend) field, which allows you to inherit the settings from another
`pyproject.toml` file, like so:
Arguments:
[FILES]... List of files or directories to format
```toml
# Extend the `pyproject.toml` file in the parent directory.
extend = "../pyproject.toml"
# But use a different line length.
line-length = 100
Options:
--check
Avoid writing any formatted files back; instead, exit with a non-zero status code if any files would have been modified, and zero otherwise
--diff
Avoid writing any formatted files back; instead, exit with a non-zero status code and the difference between the current file and how the formatted file would look like
--config <CONFIG>
Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
--target-version <TARGET_VERSION>
The minimum Python version that should be supported [possible values: py37, py38, py39, py310, py311, py312]
--preview
Enable preview mode; enables unstable formatting. Use `--no-preview` to disable
-h, --help
Print help
File selection:
--respect-gitignore Respect file exclusions via `.gitignore` and other standard ignore files. Use `--no-respect-gitignore` to disable
--exclude <FILE_PATTERN> List of paths, used to omit files and/or directories from analysis
--force-exclude Enforce exclusions, even for paths passed to Ruff directly on the command-line. Use `--no-force-exclude` to disable
Miscellaneous:
--isolated Ignore all configuration files
--stdin-filename <STDIN_FILENAME> The name of the file when passing it through stdin
Log levels:
-v, --verbose Enable verbose logging
-q, --quiet Print diagnostics, but nothing else
-s, --silent Disable all logging (but still exit with status code "1" upon detecting diagnostics)
```
All of the above rules apply equivalently to `ruff.toml` and `.ruff.toml` files. If Ruff detects
multiple configuration files in the same directory, the `.ruff.toml` file will take precedence over
the `ruff.toml` file, and the `ruff.toml` file will take precedence over the `pyproject.toml` file.
## Python file discovery
When passed a path on the command-line, Ruff will automatically discover all Python files in that
path, taking into account the [`exclude`](settings.md#exclude) and
[`extend-exclude`](settings.md#extend-exclude) settings in each directory's
`pyproject.toml` file.
By default, Ruff will also skip any files that are omitted via `.ignore`, `.gitignore`,
`.git/info/exclude`, and global `gitignore` files (see: [`respect-gitignore`](settings.md#respect-gitignore)).
Files that are passed to `ruff` directly are always linted, regardless of the above criteria.
For example, `ruff check /path/to/excluded/file.py` will always lint `file.py`.
## Jupyter Notebook discovery
Ruff has built-in support for linting [Jupyter Notebooks](https://jupyter.org/).
To opt in to linting Jupyter Notebook (`.ipynb`) files, add the `*.ipynb` pattern to your
[`include`](settings.md#include) setting, like so:
```toml
[tool.ruff]
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
```
This will prompt Ruff to discover Jupyter Notebook (`.ipynb`) files in any specified
directories, and lint them accordingly.
Alternatively, pass the notebook file(s) to `ruff` on the command-line directly. For example,
`ruff check /path/to/notebook.ipynb` will always lint `notebook.ipynb`.
## Rule selection
The set of enabled rules is controlled via the [`select`](settings.md#select) and
[`ignore`](settings.md#ignore) settings, along with the
[`extend-select`](settings.md#extend-select) and
[`extend-ignore`](settings.md#extend-ignore) modifiers.
To resolve the enabled rule set, Ruff may need to reconcile `select` and `ignore` from a variety
of sources, including the current `pyproject.toml`, any inherited `pyproject.toml` files, and the
CLI (e.g., `--select`).
In those scenarios, Ruff uses the "highest-priority" `select` as the basis for the rule set, and
then applies any `extend-select`, `ignore`, and `extend-ignore` adjustments. CLI options are given
higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is given higher
priority than any inherited `pyproject.toml` files.
For example, given the following `pyproject.toml` file:
```toml
[tool.ruff]
select = ["E", "F"]
ignore = ["F401"]
```
Running `ruff check --select F401` would result in Ruff enforcing `F401`, and no other rules.
Running `ruff check --extend-select B` would result in Ruff enforcing the `E`, `F`, and `B` rules,
with the exception of `F401`.
## Fixes
Ruff supports automatic fixes for a variety of lint errors. For example, Ruff can remove unused
imports, reformat docstrings, rewrite type annotations to use newer Python syntax, and more.
To enable fixes, pass the `--fix` flag to `ruff check`:
```shell
ruff check . --fix
```
By default, Ruff will fix all violations for which safe fixes are available; to determine
whether a rule supports fixing, see [_Rules_](rules.md).
### Fix safety
Ruff labels fixes as "safe" and "unsafe". The meaning and intent of your code will be retained when applying safe fixes, but the meaning could be changed when applying unsafe fixes.
For example, [`unnecessary-iterable-allocation-for-first-element`](rules/unnecessary-iterable-allocation-for-first-element.md) (`RUF015`) is a rule which checks for potentially unperformant use of `list(...)[0]`. The fix replaces this pattern with `next(iter(...))` which can result in a drastic speedup:
```shell
$ python -m timeit "head = list(range(99999999))[0]"
1 loop, best of 5: 1.69 sec per loop
```
```shell
$ python -m timeit "head = next(iter(range(99999999)))"
5000000 loops, best of 5: 70.8 nsec per loop
```
However, when the collection is empty, this changes the raised exception from an `IndexError` to `StopIteration`:
```shell
$ python -c 'list(range(0))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
IndexError: list index out of range
```
```shell
$ python -c 'next(iter(range(0)))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
StopIteration
```
Since this could break error handling, this fix is categorized as unsafe.
Ruff only enables safe fixes by default. Unsafe fixes can be enabled by settings [`unsafe-fixes`](settings.md#unsafe-fixes) in your configuration file or passing the `--unsafe-fixes` flag to `ruff check`:
```shell
# Show unsafe fixes
ruff check . --unsafe-fixes
# Apply unsafe fixes
ruff check . --fix --unsafe-fixes
```
The safety of fixes can be adjusted per rule using the [`extend-safe-fixes`](settings.md#extend-safe-fixes) and [`extend-unsafe-fixes`](settings.md#extend-unsafe-fixes) settings.
For example, the following configuration would promote unsafe fixes for `F601` to safe fixes and demote safe fixes for `UP034` to unsafe fixes:
```toml
[tool.ruff.lint]
extend-safe-fixes = ["F601"]
extend-unsafe-fixes = ["UP034"]
```
You may use prefixes to select rules as well, e.g., `F` can be used to promote fixes for all rules in Pyflakes to safe.
!!! note
All fixes will always be displayed by Ruff when using the `json` output format. The safety of each fix is available under the `applicability` field.
### Disabling fixes
To limit the set of rules that Ruff should fix, use the [`fixable`](settings.md#fixable) and [`unfixable`](settings.md#unfixable) settings, along with their [`extend-fixable`](settings.md#extend-fixable) and [`extend-unfixable`](settings.md#extend-unfixable)
variants.
For example, the following configuration would enable fixes for all rules except
[`unused-imports`](rules/unused-import.md) (`F401`):
```toml
[tool.ruff.lint]
fixable = ["ALL"]
unfixable = ["F401"]
```
Conversely, the following configuration would only enable fixes for `F401`:
```toml
[tool.ruff.lint]
fixable = ["F401"]
```
## Error suppression
To omit a lint rule entirely, add it to the "ignore" list via [`ignore`](settings.md#ignore)
or [`extend-ignore`](settings.md#extend-ignore), either on the command-line
or in your `pyproject.toml` file.
To ignore a violation inline, Ruff uses a `noqa` system similar to
[Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html). To ignore an individual
violation, add `# noqa: {code}` to the end of the line, like so:
```python
# Ignore F841.
x = 1 # noqa: F841
# Ignore E741 and F841.
i = 1 # noqa: E741, F841
# Ignore _all_ violations.
x = 1 # noqa
```
For multi-line strings (like docstrings),
the `noqa` directive should come at the end of the string (after the closing triple quote),
and will apply to the entire string, like so:
```python
"""Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.
""" # noqa: E501
```
To ignore all violations across an entire file, add `# ruff: noqa` to any line in the file, like so:
```python
# ruff: noqa
```
To ignore a specific rule across an entire file, add `# ruff: noqa: {code}` to any line in the file,
like so:
```python
# ruff: noqa: F841
```
Or see the [`per-file-ignores`](settings.md#per-file-ignores) configuration
setting, which enables the same functionality via a `pyproject.toml` file.
Note that Ruff will also respect Flake8's `# flake8: noqa` directive, and will treat it as
equivalent to `# ruff: noqa`.
### Automatic `noqa` management
Ruff supports several workflows to aid in `noqa` management.
First, Ruff provides a special rule code, `RUF100`, to enforce that your `noqa` directives are
"valid", in that the violations they _say_ they ignore are actually being triggered on that line
(and thus suppressed). You can run `ruff check /path/to/file.py --extend-select RUF100` to flag
unused `noqa` directives.
Second, Ruff can _automatically remove_ unused `noqa` directives via its fix functionality.
You can run `ruff check /path/to/file.py --extend-select RUF100 --fix` to automatically remove
unused `noqa` directives.
Third, Ruff can _automatically add_ `noqa` directives to all failing lines. This is useful when
migrating a new codebase to Ruff. You can run `ruff check /path/to/file.py --add-noqa` to
automatically add `noqa` directives to all failing lines, with the appropriate rule codes.
### Action comments
Ruff respects isort's [action comments](https://pycqa.github.io/isort/docs/configuration/action_comments.html)
(`# isort: skip_file`, `# isort: on`, `# isort: off`, `# isort: skip`, and `# isort: split`), which
enable selectively enabling and disabling import sorting for blocks of code and other inline
configuration.
Ruff will also respect variants of these action comments with a `# ruff:` prefix
(e.g., `# ruff: isort: skip_file`, `# ruff: isort: on`, and so on). These variants more clearly
convey that the action comment is intended for Ruff, but are functionally equivalent to the
isort variants.
See the [isort documentation](https://pycqa.github.io/isort/docs/configuration/action_comments.html)
for more.
## Exit codes
By default, Ruff exits with the following status codes:
- `0` if no violations were found, or if all present violations were fixed automatically.
- `1` if violations were found.
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an
internal error.
This convention mirrors that of tools like ESLint, Prettier, and RuboCop.
Ruff supports two command-line flags that alter its exit code behavior:
- `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found.
Note that Ruff will still exit with a status code of `2` if it terminates abnormally.
- `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were
found, _even if_ all such violations were fixed automatically. Note that the use of
`--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after
fixing.
<!-- End auto-generated format help. -->
## Shell autocompletion

View file

@ -1,12 +1,12 @@
# FAQ
## Is Ruff compatible with Black?
## Is the Ruff linter compatible with Black?
Yes. Ruff is compatible with [Black](https://github.com/psf/black) out-of-the-box, as long as
the `line-length` setting is consistent between the two.
Yes. The Ruff linter is compatible with [Black](https://github.com/psf/black) out-of-the-box, as
long as the `line-length` setting is consistent between the two.
As a project, Ruff is designed to be used alongside Black and, as such, will defer implementing
stylistic lint rules that are obviated by automated formatting.
Ruff is designed to be used alongside a formatter (like Ruff's own formatter, or Black) and, as
such, will defer implementing stylistic rules that are obviated by automated formatting.
Note that Ruff and Black treat line-length enforcement a little differently. Black makes a
best-effort attempt to adhere to the `line-length`, but avoids automatic line-wrapping in some cases
@ -14,10 +14,21 @@ best-effort attempt to adhere to the `line-length`, but avoids automatic line-wr
the `line-length` setting. As such, if `E501` is enabled, Ruff can still trigger line-length
violations even when Black is enabled.
## How does Ruff compare to Flake8?
## How does Ruff's formatter compare to Black?
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
automatically convert your existing configuration.)
The Ruff formatter is designed to be a drop-in replacement for [Black](https://github.com/psf/black).
Specifically, the formatter is intended to emit near-identical output when run over Black-formatted
code. When run over extensive Black-formatted projects like Django and Zulip, > 99.9% of lines
are formatted identically. When migrating an existing project from Black to Ruff, you should expect
to see a few differences on the margins, but the vast majority of your code should be unchanged.
When run over _non_-Black-formatted code, the formatter makes some different decisions than Black,
and so more deviations should be expected, especially around the treatment of end-of-line comments.
See [_Black compatibility_](formatter.md#black-compatibility) for more.
## How does Ruff's linter compare to Flake8?
Ruff can be used as a drop-in replacement for Flake8 when used (1) without or with a small number of
plugins, (2) alongside Black, and (3) on Python 3 code.
@ -98,10 +109,10 @@ There are a few other minor incompatibilities between Ruff and the originating F
code. (This is often solved by modifying the `src` property, e.g., to `src = ["src"]`, if your
code is nested in a `src` directory.)
## How does Ruff compare to Pylint?
## How does Ruff's linter compare to Pylint?
At time of writing, Pylint implements ~409 total rules, while Ruff implements 440, of which at least
89 overlap with the Pylint rule set (you can find the mapping in [#970](https://github.com/astral-sh/ruff/issues/970)).
At time of writing, Pylint implements ~409 total rules, while Ruff implements over 700, of which at
least 172 overlap with the Pylint rule set (see: [#970](https://github.com/astral-sh/ruff/issues/970)).
Pylint implements many rules that Ruff does not, and vice versa. For example, Pylint does more type
inference than Ruff (e.g., Pylint can validate the number of arguments in a function call). As such,
@ -182,13 +193,18 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [pydocstyle](https://pypi.org/project/pydocstyle/)
- [tryceratops](https://pypi.org/project/tryceratops/)
Ruff can also replace [isort](https://pypi.org/project/isort/),
Ruff can also replace [Black](https://pypi.org/project/black/), [isort](https://pypi.org/project/isort/),
[yesqa](https://github.com/asottile/yesqa), [eradicate](https://pypi.org/project/eradicate/), and
most of the rules implemented in [pyupgrade](https://pypi.org/project/pyupgrade/).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an
[issue](https://github.com/astral-sh/ruff/issues/new).
## Do I have to use Ruff's linter and formatter together?
Nope! Ruff's linter and formatter can be used independently of one another -- you can use
Ruff as a formatter, but not a linter, or vice versa.
## What versions of Python does Ruff support?
Ruff can lint code for any Python version from 3.7 onwards, including Python 3.12.
@ -209,7 +225,7 @@ pip install ruff
Ruff ships with wheels for all major platforms, which enables `pip` to install Ruff without relying
on Rust at all.
## Can I write my own plugins for Ruff?
## Can I write my own linter plugins for Ruff?
Ruff does not yet support third-party plugins, though a plugin system is within-scope for the
project. See [#283](https://github.com/astral-sh/ruff/issues/283) for more.
@ -326,18 +342,19 @@ For a detailed explanation, see the [contributing guide](contributing.md).
Ruff has built-in support for linting [Jupyter Notebooks](https://jupyter.org/).
To opt in to linting Jupyter Notebook (`.ipynb`) files, add the `*.ipynb` pattern to your
[`include`](settings.md#include) setting, like so:
[`extend-include`](settings.md#extend-include) setting, like so:
```toml
[tool.ruff]
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
extend-include = ["*.ipynb"]
```
This will prompt Ruff to discover Jupyter Notebook (`.ipynb`) files in any specified
directories, and lint them accordingly.
directories, then lint and format them accordingly.
Alternatively, pass the notebook file(s) to `ruff` on the command-line directly. For example,
`ruff check /path/to/notebook.ipynb` will always lint `notebook.ipynb`.
`ruff check /path/to/notebook.ipynb` will always lint `notebook.ipynb`. Similarly,
`ruff format /path/to/notebook.ipynb` will always format `notebook.ipynb`.
Ruff also integrates with [nbQA](https://github.com/nbQA-dev/nbQA), a tool for running linters and
code formatters over Jupyter Notebooks.
@ -384,7 +401,7 @@ matter how they're provided, which avoids accidental incompatibilities and simpl
By default, no `convention` is set, and so the enabled rules are determined by the `select` setting
alone.
## What is preview?
## What is "preview"?
Preview enables a collection of newer rules and fixes that are considered experimental or unstable.
See the [preview documentation](preview.md) for more details; or, to see which rules are currently
@ -394,7 +411,7 @@ in preview, visit the [rules reference](rules.md).
Run `ruff check /path/to/code.py --show-settings` to view the resolved settings for a given file.
## I want to use Ruff, but I don't want to use `pyproject.toml`. Is that possible?
## I want to use Ruff, but I don't want to use `pyproject.toml`. What are my options?
Yes! In lieu of a `pyproject.toml` file, you can use a `ruff.toml` file for configuration. The two
files are functionally equivalent and have an identical schema, with the exception that a `ruff.toml`
@ -434,7 +451,7 @@ On Windows, Ruff expects that file to be located at `C:\Users\Alice\AppData\Roam
For more, see the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) crate.
## Ruff tried to fix something — but it broke my code?
## Ruff tried to fix something — but it broke my code. What's going on?
Ruff labels fixes as "safe" and "unsafe". By default, Ruff will fix all violations for which safe
fixes are available, while unsafe fixes can be enabled via the [`unsafe-fixes`](settings.md#unsafe-fixes)

263
docs/formatter.md Normal file
View file

@ -0,0 +1,263 @@
# The Ruff Formatter
The Ruff formatter is an extremely fast Python code formatter designed as a drop-in replacement for
[Black](https://pypi.org/project/black/), available as part of the `ruff` CLI (as of Ruff v0.0.289).
## `ruff format`
`ruff format` is the primary entrypoint to the formatter. It accepts a list of files or
directories, and formats all discovered Python files:
```shell
ruff format . # Format all files in the current directory.
ruff format /path/to/file.py # Format a single file.
```
Similar to Black, running `ruff format /path/to/file.py` will format the given file or directory
in-place, while `ruff format --check /path/to/file.py` will avoid writing any formatted files back,
and instead exit with a non-zero status code upon detecting any unformatted files.
For the full list of supported options, run `ruff format --help`.
## Philosophy
The initial goal of the Ruff formatter is _not_ to innovate on code style, but rather, to innovate
on performance, and provide a unified toolchain across Ruff's linter, formatter, and any and all
future tools.
As such, the formatter is designed as a drop-in replacement for [Black](https://github.com/psf/black),
but with an excessive focus on performance and direct integration with Ruff. Given Black's
popularity within the Python ecosystem, targeting Black compatibility ensures that formatter
adoption is minimally disruptive for the vast majority of projects.
Specifically, the formatter is intended to emit near-identical output when run over existing
Black-formatted code. When run over extensive Black-formatted projects like Django and Zulip, > 99.9%
of lines are formatted identically. (See: [_Black compatibility_](#black-compatibility).)
Given this focus on Black compatibility, the formatter thus adheres to [Black's (stable) code style](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html),
which aims for "consistency, generality, readability and reducing git diffs". To give you a sense
for the enforced code style, here's an example:
```python
# Input
def _make_ssl_transport(
rawsock, protocol, sslcontext, waiter=None,
*, server_side=False, server_hostname=None,
extra=None, server=None,
ssl_handshake_timeout=None,
call_connection_made=True):
'''Make an SSL transport.'''
if waiter is None:
waiter = Future(loop=loop)
if extra is None:
extra = {}
...
# Ruff
def _make_ssl_transport(
rawsock,
protocol,
sslcontext,
waiter=None,
*,
server_side=False,
server_hostname=None,
extra=None,
server=None,
ssl_handshake_timeout=None,
call_connection_made=True,
):
"""Make an SSL transport."""
if waiter is None:
waiter = Future(loop=loop)
if extra is None:
extra = {}
...
```
Like Black, the Ruff formatter does _not_ support extensive code style configuration; however,
unlike Black, it _does_ support configuring the desired quote style, indent style, line endings,
and more. (See: [_Configuration_](#configuration).)
While the formatter is designed to be a drop-in replacement for Black, it is not intended to be
used interchangeably with Black on an ongoing basis, as the formatter _does_ differ from
Black in a few conscious ways (see: [_Known deviations_](formatter/black.md)). In general,
deviations are limited to cases in which Ruff's behavior was deemed more consistent, or
significantly simpler to support (with negligible end-user impact) given the differences in the
underlying implementations between Black and Ruff.
Going forward, the Ruff Formatter will support Black's preview style under Ruff's own
[preview](preview.md) mode.
## Configuration
The Ruff Formatter exposes a small set of configuration options, some of which are also supported
by Black (like line width), some of which are unique to Ruff (like quote and indentation style).
For example, to configure the formatter to use single quotes, a line width of 100, and
tab indentation, add the following to your `pyproject.toml`:
```toml
[tool.ruff]
line-length = 100
[tool.ruff.format]
quote-style = "single"
indent-style = "tab"
```
For the full list of supported settings, see [_Settings_](settings.md#format). For more on
configuring Ruff via `pyproject.toml`, see [_Configuring Ruff_](configuration.md).
Given the focus on Black compatibility (and unlike formatters like [YAPF](https://github.com/google/yapf)),
Ruff does not currently expose any configuration options to modify core formatting behavior outside
of these trivia-related settings.
## Format suppression
Like Black, Ruff supports `# fmt: on`, `# fmt: off`, and `# fmt: skip` pragma comments, which can
be used to temporarily disable formatting for a given code block.
`# fmt: on` and `# fmt: off` comments are enforced at the statement level:
```python
# fmt: off
not_formatted=3
also_not_formatted=4
# fmt: on
```
As such, adding `# fmt: on` and `# fmt: off` comments within expressions will have no effect. In
the following example, both list entries will be formatted, despite the `# fmt: off`:
```python
[
# fmt: off
'1',
# fmt: on
'2',
]
```
Instead, apply the `# fmt: off` comment to the entire statement:
```python
# fmt: off
[
'1',
'2',
]
# fmt: on
```
`# fmt: skip` comments suppress formatting for a preceding statement, case header, decorator,
function definition, or class definition:
```python
if True:
pass
elif False: # fmt: skip
pass
@Test
@Test2 # fmt: off
def test(): ...
a = [1, 2, 3, 4, 5] # fmt: off
def test(a, b, c, d, e, f) -> int: # fmt: skip
pass
```
Like Black, Ruff will _also_ recognize [YAPF](https://github.com/google/yapf)'s `# yapf: disable` and `# yapf: enable` pragma
comments, which are treated equivalently to `# fmt: off` and `# fmt: on`, respectively.
## Conflicting lint rules
Ruff's formatter is designed to be used alongside the linter. However, the linter includes
some rules that, when enabled, can cause conflicts with the formatter, leading to unexpected
behavior.
When using Ruff as a formatter, we recommend disabling the following rules:
- [`tab-indentation`](rules/tab-indentation.md) (`W191`)
- [`indentation-with-invalid-multiple`](rules/indentation-with-invalid-multiple.md) (`E111`)
- [`indentation-with-invalid-multiple-comment`](rules/indentation-with-invalid-multiple-comment.md) (`E114`)
- [`over-indented`](rules/over-indented.md) (`E117`)
- [`indent-with-spaces`](rules/indent-with-spaces.md) (`D206`)
- [`triple-single-quotes`](rules/triple-single-quotes.md) (`D300`)
- [`bad-quotes-inline-string`](rules/bad-quotes-inline-string.md) (`Q000`)
- [`bad-quotes-multiline-string`](rules/bad-quotes-multiline-string.md) (`Q001`)
- [`bad-quotes-docstring`](rules/bad-quotes-docstring.md) (`Q002`)
- [`avoidable-escaped-quote`](rules/avoidable-escaped-quote.md) (`Q003`)
- [`missing-trailing-comma`](rules/missing-trailing-comma.md) (`COM812`)
- [`prohibited-trailing-comma`](rules/prohibited-trailing-comma.md) (`COM819`)
- [`single-line-implicit-string-concatenation`](rules/single-line-implicit-string-concatenation.md) (`ISC001`)
- [`multi-line-implicit-string-concatenation`](rules/multi-line-implicit-string-concatenation.md) (`ISC002`)
Similarly, we recommend disabling the following isort settings, which are incompatible with the
formatter's treatment of import statements when set to non-default values:
- [`force-single-line`](settings.md#isort-force-single-line)
- [`force-wrap-aliases`](settings.md#isort-force-wrap-aliases)
- [`lines-after-imports`](settings.md#isort-lines-after-imports)
- [`lines-between-types`](settings.md#isort-lines-between-types)
- [`split-on-trailing-comma`](settings.md#isort-split-on-trailing-comma)
## Exit codes
`ruff format` exits with the following status codes:
- `0` if Ruff terminates successfully, regardless of whether any files were formatted.
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an
internal error.
Meanwhile, `ruff format --check` exits with the following status codes:
- `0` if Ruff terminates successfully, and no files would be formatted if `--check` were not
specified.
- `1` if Ruff terminates successfully, and one or more files would be formatted if `--check` were
not specified.
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an
internal error.
## Black compatibility
The formatter is designed to be a drop-in replacement for [Black](https://github.com/psf/black).
Specifically, the formatter is intended to emit near-identical output when run over Black-formatted
code. When run over extensive Black-formatted projects like Django and Zulip, > 99.9% of lines
are formatted identically. When migrating an existing project from Black to Ruff, you should expect
to see a few differences on the margins, but the vast majority of your code should be unchanged.
When run over _non_-Black-formatted code, the formatter makes some different decisions than Black,
and so more deviations should be expected, especially around the treatment of end-of-line comments.
If you identify deviations in your project, spot-check them against the [known deviations](formatter/black.md),
as well as the [unintentional deviations](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3Aformatter)
filed in the issue tracker. If you've identified a new deviation, please [file an issue](https://github.com/astral-sh/ruff/issues/new).
### Intentional deviations
While the Ruff formatter aims to be a drop-in replacement for Black, it does differ from Black
in a few known ways. Some of these differences emerge from conscious attempts to improve upon
Black's code style, while others fall out of differences in the underlying implementations.
For a complete enumeration of these intentional deviations, see [_Known deviations_](formatter/black.md).
Unintentional deviations from Black are tracked in the [issue tracker](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3Aformatter).
### Preview style
Black gates formatting changes behind a [`preview`](https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html#preview-style)
flag. The formatter does not yet support Black's preview style, though the intention is to support
it within the coming months behind Ruff's own [`preview`](https://docs.astral.sh/ruff/settings/#preview)
flag.
Black promotes some of its preview styling to stable at the end of each year. Ruff will similarly
implement formatting changes under the [`preview`](https://docs.astral.sh/ruff/settings/#preview)
flag, promoting them to stable through minor releases, in accordance with our [versioning policy](https://github.com/astral-sh/ruff/discussions/6998#discussioncomment-7016766).

532
docs/formatter/black.md Normal file
View file

@ -0,0 +1,532 @@
---
title: "Known Deviations from Black"
hide:
- navigation
---
This document enumerates the known, intentional differences in code style between Black and Ruff's
formatter.
For a list of unintentional deviations, see [issue tracker](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3Aformatter).
### Trailing end-of-line comments
Black's priority is to fit an entire statement on a line, even if it contains end-of-line comments.
In such cases, Black collapses the statement, and moves the comment to the end of the collapsed
statement:
```python
# Input
while (
cond1 # almost always true
and cond2 # almost never true
):
print("Do something")
# Black
while cond1 and cond2: # almost always true # almost never true
print("Do something")
```
Ruff, like [Prettier](https://prettier.io/), expands any statement that contains trailing
end-of-line comments. For example, Ruff would avoid collapsing the `while` test in the snippet
above. This ensures that the comments remain close to their original position and retain their
original intent, at the cost of retaining additional vertical space.
This deviation only impacts unformatted code, in that Ruff's output should not deviate for code that
has already been formatted by Black.
### Pragma comments are ignored when computing line width
Pragma comments (`# type`, `# noqa`, `# pyright`, `# pylint`, etc.) are ignored when computing the width of a line.
This prevents Ruff from moving pragma comments around, thereby modifying their meaning and behavior:
See Ruff's [pragma comment handling proposal](https://github.com/astral-sh/ruff/discussions/6670)
for details.
This is similar to [Pyink](https://github.com/google/pyink) but a deviation from Black. Black avoids
splitting any lines that contain a `# type` comment ([#997](https://github.com/psf/black/issues/997)),
but otherwise avoids special-casing pragma comments.
As Ruff expands trailing end-of-line comments, Ruff will also avoid moving pragma comments in cases
like the following, where moving the `# noqa` to the end of the line causes it to suppress errors
on both `first()` and `second()`:
```python
# Input
[
first(), # noqa
second()
]
# Black
[first(), second()] # noqa
# Ruff
[
first(), # noqa
second(),
]
```
### Line width vs. line length
Ruff uses the Unicode width of a line to determine if a line fits. Black's stable style uses
character width, while Black's preview style uses Unicode width for strings ([#3445](https://github.com/psf/black/pull/3445)),
and character width for all other tokens. Ruff's behavior is closer to Black's preview style than
Black's stable style, although Ruff _also_ uses Unicode width for identifiers and comments.
### Walruses in slice expressions
Black avoids inserting space around `:=` operators within slices. For example, the following adheres
to Black stable style:
```python
# Input
x[y:=1]
# Black
x[y:=1]
```
Ruff will instead add space around the `:=` operator:
```python
# Input
x[y:=1]
# Ruff
x[y := 1]
```
This will likely be incorporated into Black's preview style ([#3823](https://github.com/psf/black/pull/3823)).
### `global` and `nonlocal` names are broken across multiple lines by continuations
If a `global` or `nonlocal` statement includes multiple names, and exceeds the configured line
width, Ruff will break them across multiple lines using continuations:
```python
# Input
global analyze_featuremap_layer, analyze_featuremapcompression_layer, analyze_latencies_post, analyze_motions_layer, analyze_size_model
# Ruff
global \
analyze_featuremap_layer, \
analyze_featuremapcompression_layer, \
analyze_latencies_post, \
analyze_motions_layer, \
analyze_size_model
```
### Newlines are inserted after all class docstrings
Black typically enforces a single newline after a class docstring. However, it does not apply such
formatting if the docstring is single-quoted rather than triple-quoted, while Ruff enforces a
single newline in both cases:
```python
# Input
class IntFromGeom(GEOSFuncFactory):
"Argument is a geometry, return type is an integer."
argtypes = [GEOM_PTR]
restype = c_int
errcheck = staticmethod(check_minus_one)
# Black
class IntFromGeom(GEOSFuncFactory):
"Argument is a geometry, return type is an integer."
argtypes = [GEOM_PTR]
restype = c_int
errcheck = staticmethod(check_minus_one)
# Ruff
class IntFromGeom(GEOSFuncFactory):
"Argument is a geometry, return type is an integer."
argtypes = [GEOM_PTR]
restype = c_int
errcheck = staticmethod(check_minus_one)
```
### Trailing own-line comments on imports are not moved to the next line
Black enforces a single empty line between an import and a trailing own-line comment. Ruff leaves
such comments in-place:
```python
# Input
import os
# comment
import sys
# Black
import os
# comment
import sys
# Ruff
import os
# comment
import sys
```
### Parentheses around awaited collections are not preserved
Black preserves parentheses around awaited collections:
```python
await ([1, 2, 3])
```
Ruff will instead remove them:
```python
await [1, 2, 3]
```
This is more consistent to the formatting of other awaited expressions: Ruff and Black both
remove parentheses around, e.g., `await (1)`, only retaining them when syntactically required,
as in, e.g., `await (x := 1)`.
### Implicit string concatenations in attribute accesses
Given the following unformatted code:
```python
print("aaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaa".format(bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb))
```
Internally, Black's logic will first expand the outermost `print` call:
```python
print(
"aaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaa".format(bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb)
)
```
Since the argument is _still_ too long, Black will then split on the operator with the highest split
precedence. In this case, Black splits on the implicit string concatenation, to produce the
following Black-formatted code:
```python
print(
"aaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaa".format(bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb)
)
```
Ruff gives implicit concatenations a "lower" priority when breaking lines. As a result, Ruff
would instead format the above as:
```python
print(
"aaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaa".format(
bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb
)
)
```
In general, Black splits implicit string concatenations over multiple lines more often than Ruff,
even if those concatenations _can_ fit on a single line. Ruff instead avoids splitting such
concatenations unless doing so is necessary to fit within the configured line width.
### Own-line comments on expressions don't cause the expression to expand
Given an expression like:
```python
(
# A comment in the middle
some_example_var and some_example_var not in some_example_var
)
```
Black associates the comment with `some_example_var`, thus splitting it over two lines:
```python
(
# A comment in the middle
some_example_var
and some_example_var not in some_example_var
)
```
Ruff will instead associate the comment with the entire boolean expression, thus preserving the
initial formatting:
```python
(
# A comment in the middle
some_example_var and some_example_var not in some_example_var
)
```
### Tuples are parenthesized when expanded
Ruff tends towards parenthesizing tuples (with a few exceptions), while Black tends to remove tuple
parentheses more often.
In particular, Ruff will always insert parentheses around tuples that expand over multiple lines:
```python
# Input
(a, b), (c, d,)
# Black
(a, b), (
c,
d,
)
# Ruff
(
(a, b),
(
c,
d,
),
)
```
There's one exception here. In `for` loops, both Ruff and Black will avoid inserting unnecessary
parentheses:
```python
# Input
for a, f(b,) in c:
pass
# Black
for a, f(
b,
) in c:
pass
# Ruff
for a, f(
b,
) in c:
pass
```
### Single-element tuples are always parenthesized
Ruff always inserts parentheses around single-element tuples, while Black will omit them in some
cases:
```python
# Input
(a, b),
# Black
(a, b),
# Ruff
((a, b),)
```
Adding parentheses around single-element tuples adds visual distinction and helps avoid "accidental"
tuples created by extraneous trailing commas (see, e.g., [#17181](https://github.com/django/django/pull/17181)).
### Trailing commas are inserted when expanding a function definition with a single argument
When a function definition with a single argument is expanded over multiple lines, Black
will add a trailing comma in some cases, depending on whether the argument includes a type
annotation and/or a default value.
For example, Black will add a trailing comma to the first and second function definitions below,
but not the third:
```python
def func(
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
) -> None:
...
def func(
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=1,
) -> None:
...
def func(
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Argument(
"network_messages.pickle",
help="The path of the pickle file that will contain the network messages",
) = 1
) -> None:
...
```
Ruff will instead insert a trailing comma in all such cases for consistency.
### Parentheses around call-chain assignment values are not preserved
Given:
```python
def update_emission_strength():
(
get_rgbw_emission_node_tree(self)
.nodes["Emission"]
.inputs["Strength"]
.default_value
) = (self.emission_strength * 2)
```
Black will preserve the parentheses in `(self.emission_strength * 2)`, whereas Ruff will remove
them.
Both Black and Ruff remove such parentheses in simpler assignments, like:
```python
# Input
def update_emission_strength():
value = (self.emission_strength * 2)
# Black
def update_emission_strength():
value = self.emission_strength * 2
# Ruff
def update_emission_strength():
value = self.emission_strength * 2
```
### Type annotations may be parenthesized when expanded
Black will avoid parenthesizing type annotations in an annotated assignment, while Ruff will insert
parentheses in some cases.
For example:
```python
# Black
StartElementHandler: Callable[[str, dict[str, str]], Any] | Callable[[str, list[str]], Any] | Callable[
[str, dict[str, str], list[str]], Any
] | None
# Ruff
StartElementHandler: (
Callable[[str, dict[str, str]], Any]
| Callable[[str, list[str]], Any]
| Callable[[str, dict[str, str], list[str]], Any]
| None
)
```
### Call chain calls break differently
Black occasionally breaks call chains differently than Ruff; in particular, Black occasionally
expands the arguments for the last call in the chain, as in:
```python
# Input
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates().rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False)
# Black
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates().rename(
columns={
"a": "a",
}
).to_csv(
path / "aaaaaa.csv", index=False
)
# Ruff
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates().rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False)
```
Ruff will only expand the arguments if doing so is necessary to fit within the configured line
width.
Note that Black does not apply this last-call argument breaking universally. For example, both
Black and Ruff will format the following identically:
```python
# Input
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates(a).rename(
columns={
"a": "a",
}
).to_csv(
path / "aaaaaa.csv", index=False
).other(a)
# Black
df.drop(columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]).drop_duplicates(a).rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False).other(a)
# Ruff
df.drop(columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]).drop_duplicates(a).rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False).other(a)
```
### Expressions with (non-pragma) trailing comments are split more often
Both Ruff and Black will break the following expression over multiple lines, since it then allows
the expression to fit within the configured line width:
```python
# Input
some_long_variable_name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
# Black
some_long_variable_name = (
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)
# Ruff
some_long_variable_name = (
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)
```
However, if the expression ends in a trailing comment, Black will avoid wrapping the expression
in some cases, while Ruff will wrap as long as it allows the expanded lines to fit within the line
length limit:
```python
# Input
some_long_variable_name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # a trailing comment
# Black
some_long_variable_name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # a trailing comment
# Ruff
some_long_variable_name = (
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
) # a trailing comment
```
Doing so leads to fewer overlong lines while retaining the comment's intent. As pragma comments
(like `# noqa` and `# type: ignore`) are ignored when computing line width, this behavior only
applies to non-pragma comments.

View file

@ -6,6 +6,13 @@ Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
pip install ruff
```
Once installed, you can run Ruff from the command line:
```shell
ruff check . # Lint all files in the current directory.
ruff format . # Format all files in the current directory.
```
For **macOS Homebrew** and **Linuxbrew** users, Ruff is also available as [`ruff`](https://formulae.brew.sh/formula/ruff)
on Homebrew:

View file

@ -1,4 +1,4 @@
# Editor Integrations
# Integrations
## VS Code (Official)
@ -7,6 +7,54 @@ which supports fix actions, import sorting, and more.
![Ruff VS Code extension](https://user-images.githubusercontent.com/1309177/205175763-cf34871d-5c05-4abf-9916-440afc82dbf8.gif)
## pre-commit
Ruff can be used as a [pre-commit](https://pre-commit.com) hook via [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit):
```yaml
# Run the Ruff formatter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.291
hooks:
- id: ruff-format
# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.291
hooks:
- id: ruff
```
To enable fixes, add the `--fix` argument to the linter:
```yaml
# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.291
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
```
To run the hooks over Jupyter Notebooks too, add `jupyter` to the list of allowed filetypes:
```yaml
# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.291
hooks:
- id: ruff
types_or: [python, pyi, jupyter]
```
Ruff's lint hook should be placed after other formatting tools, such as Ruff's format hook, Black,
or isort, _unless_ you enable autofix, in which case, Ruff's pre-commit hook should run _before_
Black, isort, and other formatting tools, as Ruff's autofix behavior can output code changes that
require reformatting.
## Language Server Protocol (Official)
Ruff supports the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/)
@ -296,3 +344,49 @@ jobs:
- name: Run Ruff
run: ruff check --output-format=github .
```
Ruff can also be used as a GitHub Action via [`ruff-action`](https://github.com/chartboost/ruff-action).
By default, `ruff-action` runs as a pass-fail test to ensure that a given repository doesn't contain
any lint rule violations as per its [configuration](https://github.com/astral-sh/ruff/blob/main/docs/configuration.md).
However, under-the-hood, `ruff-action` installs and runs `ruff` directly, so it can be used to
execute any supported `ruff` command (e.g., `ruff check --fix`).
`ruff-action` supports all GitHub-hosted runners, and can be used with any published Ruff version
(i.e., any version available on [PyPI](https://pypi.org/project/ruff/)).
To use `ruff-action`, create a file (e.g., `.github/workflows/ruff.yml`) inside your repository
with:
```yaml
name: Ruff
on: [ push, pull_request ]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: chartboost/ruff-action@v1
```
Alternatively, you can include `ruff-action` as a step in any other workflow file:
```yaml
- uses: chartboost/ruff-action@v1
```
`ruff-action` accepts optional configuration parameters via `with:`, including:
- `version`: The Ruff version to install (default: latest).
- `options`: The command-line arguments to pass to Ruff (default: `"check"`).
- `src`: The source paths to pass to Ruff (default: `"."`).
For example, to run `ruff check --select B ./src` using Ruff version `0.0.259`:
```yaml
- uses: chartboost/ruff-action@v1
with:
src: "./src"
version: 0.0.259
args: --select B
```

295
docs/linter.md Normal file
View file

@ -0,0 +1,295 @@
# The Ruff Linter
The Ruff Linter is an extremely fast Python linter designed as a drop-in replacement for [Flake8](https://pypi.org/project/flake8/)
(plus dozens of plugins), [isort](https://pypi.org/project/isort/), [pydocstyle](https://pypi.org/project/pydocstyle/),
[pyupgrade](https://pypi.org/project/pyupgrade/), [autoflake](https://pypi.org/project/autoflake/),
and more.
## `ruff check`
`ruff check` is the primary entrypoint to the Ruff linter. It accepts a list of files or
directories, and lints all discovered Python files, optionally fixing any fixable errors:
```shell
ruff check . # Lint all files in the current directory.
ruff check . --fix # Lint all files in the current directory, and fix any fixable errors.
ruff check . --watch # Lint all files in the current directory, and re-lint on change.
```
For the full list of supported options, run `ruff check --help`.
## Rule selection
The set of enabled rules is controlled via the [`select`](settings.md#select) and [`ignore`](settings.md#ignore)
settings, along with the [`extend-select`](settings.md#extend-select) and [`extend-ignore`](settings.md#extend-ignore)
modifiers.
Ruff's linter mirrors Flake8's rule code system, in which each rule code consists of a one-to-three
letter prefix, followed by three digits (e.g., `F401`). The prefix indicates that "source" of the rule
(e.g., `F` for Pyflakes, `E` for pycodestyle, `ANN` for flake8-annotations).
Rule selectors like [`select`](settings.md#select) and [`ignore`](settings.md#ignore) accept either
a full rule code (e.g., `F401`) or any valid prefix (e.g., `F`). For example, given the following
`pyproject.toml` file:
```toml
[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"]
```
Ruff would enable all rules with the `E` (pycodestyle) or `F` (Pyflakes) prefix, with the exception
of `F401`. For more on configuring Ruff via `pyproject.toml`, see [_Configuring Ruff_](configuration.md).
As a special-case, Ruff also supports the `ALL` code, which enables all rules. Note that some
pydocstyle rules conflict (e.g., `D203` and `D211`) as they represent alternative docstring
formats. Ruff will automatically disable any conflicting rules when `ALL` is enabled.
If you're wondering how to configure Ruff, here are some **recommended guidelines**:
- Prefer `select` and `ignore` over `extend-select` and `extend-ignore`, to make your rule set
explicit.
- Use `ALL` with discretion. Enabling `ALL` will implicitly enable new rules whenever you upgrade.
- Start with a small set of rules (`select = ["E", "F"]`) and add a category at-a-time. For example,
you might consider expanding to `select = ["E", "F", "B"]` to enable the popular flake8-bugbear
extension.
For example, a configuration that enables some of the most popular rules (without being too
pedantic) might look like the following:
```toml
[tool.ruff.lint]
select = [
# pycodestyle
"E",
# Pyflakes
"F",
# pyupgrade
"UP",
# flake8-bugbear
"B",
# flake8-simplify
"SIM",
# isort
"I",
]
```
To resolve the enabled rule set, Ruff may need to reconcile `select` and `ignore` from a variety
of sources, including the current `pyproject.toml`, any inherited `pyproject.toml` files, and the
CLI (e.g., `--select`).
In those scenarios, Ruff uses the "highest-priority" `select` as the basis for the rule set, and
then applies any `extend-select`, `ignore`, and `extend-ignore` adjustments. CLI options are given
higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is given higher
priority than any inherited `pyproject.toml` files.
For example, given the following `pyproject.toml` file:
```toml
[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"]
```
Running `ruff check --select F401` would result in Ruff enforcing `F401`, and no other rules.
Running `ruff check --extend-select B` would result in Ruff enforcing the `E`, `F`, and `B` rules,
with the exception of `F401`.
## Fixes
Ruff supports automatic fixes for a variety of lint errors. For example, Ruff can remove unused
imports, reformat docstrings, rewrite type annotations to use newer Python syntax, and more.
To enable fixes, pass the `--fix` flag to `ruff check`:
```shell
ruff check . --fix
```
By default, Ruff will fix all violations for which safe fixes are available; to determine
whether a rule supports fixing, see [_Rules_](rules.md).
### Fix safety
Ruff labels fixes as "safe" and "unsafe". The meaning and intent of your code will be retained when applying safe fixes, but the meaning could be changed when applying unsafe fixes.
For example, [`unnecessary-iterable-allocation-for-first-element`](rules/unnecessary-iterable-allocation-for-first-element.md) (`RUF015`) is a rule which checks for potentially unperformant use of `list(...)[0]`. The fix replaces this pattern with `next(iter(...))` which can result in a drastic speedup:
```shell
$ python -m timeit "head = list(range(99999999))[0]"
1 loop, best of 5: 1.69 sec per loop
```
```shell
$ python -m timeit "head = next(iter(range(99999999)))"
5000000 loops, best of 5: 70.8 nsec per loop
```
However, when the collection is empty, this changes the raised exception from an `IndexError` to `StopIteration`:
```shell
$ python -c 'list(range(0))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
IndexError: list index out of range
```
```shell
$ python -c 'next(iter(range(0)))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
StopIteration
```
Since this could break error handling, this fix is categorized as unsafe.
Ruff only enables safe fixes by default. Unsafe fixes can be enabled by settings [`unsafe-fixes`](settings.md#unsafe-fixes) in your configuration file or passing the `--unsafe-fixes` flag to `ruff check`:
```shell
# Show unsafe fixes
ruff check . --unsafe-fixes
# Apply unsafe fixes
ruff check . --fix --unsafe-fixes
```
The safety of fixes can be adjusted per rule using the [`extend-safe-fixes`](settings.md#extend-safe-fixes) and [`extend-unsafe-fixes`](settings.md#extend-unsafe-fixes) settings.
For example, the following configuration would promote unsafe fixes for `F601` to safe fixes and demote safe fixes for `UP034` to unsafe fixes:
```toml
[tool.ruff.lint]
extend-safe-fixes = ["F601"]
extend-unsafe-fixes = ["UP034"]
```
You may use prefixes to select rules as well, e.g., `F` can be used to promote fixes for all rules in Pyflakes to safe.
!!! note
All fixes will always be displayed by Ruff when using the `json` output format. The safety of each fix is available under the `applicability` field.
### Disabling fixes
To limit the set of rules that Ruff should fix, use the [`fixable`](settings.md#fixable) and [`unfixable`](settings.md#unfixable) settings, along with their [`extend-fixable`](settings.md#extend-fixable) and [`extend-unfixable`](settings.md#extend-unfixable)
variants.
For example, the following configuration would enable fixes for all rules except
[`unused-imports`](rules/unused-import.md) (`F401`):
```toml
[tool.ruff.lint]
fixable = ["ALL"]
unfixable = ["F401"]
```
Conversely, the following configuration would only enable fixes for `F401`:
```toml
[tool.ruff.lint]
fixable = ["F401"]
```
## Error suppression
Ruff supports several mechanisms for suppressing lint errors, be they false positives or
permissible violations.
To omit a lint rule entirely, add it to the "ignore" list via the [`ignore`](settings.md#ignore)
or [`extend-ignore`](settings.md#extend-ignore) settings, either on the command-line
or in your `pyproject.toml` or `ruff.toml` file.
To suppress a violation inline, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
To ignore an individual violation, add `# noqa: {code}` to the end of the line, like so:
```python
# Ignore F841.
x = 1 # noqa: F841
# Ignore E741 and F841.
i = 1 # noqa: E741, F841
# Ignore _all_ violations.
x = 1 # noqa
```
For multi-line strings (like docstrings), the `noqa` directive should come at the end of the string
(after the closing triple quote), and will apply to the entire string, like so:
```python
"""Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.
""" # noqa: E501
```
To ignore all violations across an entire file, add `# ruff: noqa` to any line in the file, like so:
```python
# ruff: noqa
```
To ignore a specific rule across an entire file, add `# ruff: noqa: {code}` to any line in the file,
like so:
```python
# ruff: noqa: F841
```
Or see the [`per-file-ignores`](settings.md#per-file-ignores) setting, which enables the same
functionality from within your `pyproject.toml` or `ruff.toml` file.
Note that Ruff will also respect Flake8's `# flake8: noqa` directive, and will treat it as
equivalent to `# ruff: noqa`.
### Detecting unused suppression comments
Ruff implements a special rule, [`unused-noqa`](https://docs.astral.sh/ruff/rules/unused-noqa/),
under the `RUF100` code, to enforce that your `noqa` directives are "valid", in that the violations
they _say_ they ignore are actually being triggered on that line (and thus suppressed). To flag
unused `noqa` directives, run: `ruff check /path/to/file.py --extend-select RUF100`.
Ruff can also _remove_ any unused `noqa` directives via its fix functionality. To remove any
unused `noqa` directives, run: `ruff check /path/to/file.py --extend-select RUF100 --fix`.
### Inserting necessary suppression comments
Ruff can _automatically add_ `noqa` directives to all lines that contain violations, which is
useful when migrating a new codebase to Ruff. To automatically add `noqa` directives to all
relevant lines (with the appropriate rule codes), run: `ruff check /path/to/file.py --add-noqa`.
### Action comments
Ruff respects isort's [action comments](https://pycqa.github.io/isort/docs/configuration/action_comments.html)
(`# isort: skip_file`, `# isort: on`, `# isort: off`, `# isort: skip`, and `# isort: split`), which
enable selectively enabling and disabling import sorting for blocks of code and other inline
configuration.
Ruff will also respect variants of these action comments with a `# ruff:` prefix
(e.g., `# ruff: isort: skip_file`, `# ruff: isort: on`, and so on). These variants more clearly
convey that the action comment is intended for Ruff, but are functionally equivalent to the
isort variants.
See the [isort documentation](https://pycqa.github.io/isort/docs/configuration/action_comments.html)
for more.
## Exit codes
By default, `ruff check` exits with the following status codes:
- `0` if no violations were found, or if all present violations were fixed automatically.
- `1` if violations were found.
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an
internal error.
This convention mirrors that of tools like ESLint, Prettier, and RuboCop.
`ruff check` supports two command-line flags that alter its exit code behavior:
- `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found.
Note that Ruff will still exit with a status code of `2` if it terminates abnormally.
- `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were
found, _even if_ all such violations were fixed automatically. Note that the use of
`--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after
fixing.

View file

@ -3,7 +3,8 @@
Ruff includes an opt-in preview mode to provide an opportunity for community feedback and increase confidence that
changes are a net-benefit before enabling them for everyone.
Preview mode enables a collection of newer rules and fixes that are considered experimental or unstable.
Preview mode enables a collection of newer lint rules, fixes, and formatter style changes that are
considered experimental or unstable,.
## Enabling preview mode
@ -64,7 +65,7 @@ To see which rules are currently in preview, visit the [rules reference](rules.m
## Selecting single preview rules
When preview mode is enabled, selecting rule categories or prefixes will include all preview rules that match.
If you would prefer to opt-in to each preview rule individually, you can toggle the `explicit-preview-rules`
If you'd prefer to opt-in to each preview rule individually, you can toggle the `explicit-preview-rules`
setting in your `pyproject.toml`:
```toml

View file

@ -1,11 +1,17 @@
# Tutorial
This tutorial will walk you through the process of integrating Ruff into your project. For a more
detailed overview, see [_Configuration_](configuration.md).
This tutorial will walk you through the process of integrating Ruff's linter and formatter into
your project. For a more detailed overview, see [_Configuring Ruff_](configuration.md).
## Getting Started
Let's assume that our project structure looks like:
To start, we'll install Ruff through PyPI (or with your [preferred package manager](installation.md)):
```shell
pip install ruff
```
Let's then assume that our project structure looks like:
```text
numbers
@ -13,7 +19,7 @@ numbers
└── numbers.py
```
Where `numbers.py` contains the following code:
...where `numbers.py` contains the following code:
```py
from typing import Iterable
@ -23,16 +29,13 @@ import os
def sum_even_numbers(numbers: Iterable[int]) -> int:
"""Given an iterable of integers, return the sum of all even numbers in the iterable."""
return sum(num for num in numbers if num % 2 == 0)
return sum(
num for num in numbers
if num % 2 == 0
)
```
To start, we'll install Ruff through PyPI (or with your [preferred package manager](installation.md)):
```shell
> pip install ruff
```
We can then run Ruff over our project via `ruff check`:
We can run the Ruff linter over our project via `ruff check`:
```shell
ruff check .
@ -62,7 +65,34 @@ Running `git diff` shows the following:
def sum_even_numbers(numbers: Iterable[int]) -> int:
"""Given an iterable of integers, return the sum of all even numbers in the iterable."""
return sum(num for num in numbers if num % 2 == 0)
return sum(
num for num in numbers
if num % 2 == 0
)
```
Now that our project is passing `ruff check`, we can run the Ruff formatter via `ruff format`:
```shell
ruff format .
1 file reformatted
```
Running `git diff` shows that the `sum` call was reformatted to fit within the default 88-character
line length limit:
```diff
--- a/numbers.py
+++ b/numbers.py
@@ -3,7 +3,4 @@ from typing import Iterable
def sum_even_numbers(numbers: Iterable[int]) -> int:
"""Given an iterable of integers, return the sum of all even numbers in the iterable."""
- return sum(
- num for num in numbers
- if num % 2 == 0
- )
+ return sum(num for num in numbers if num % 2 == 0)
```
Thus far, we've been using Ruff's default configuration. Let's take a look at how we can customize
@ -73,19 +103,21 @@ Ruff's behavior.
To determine the appropriate settings for each Python file, Ruff looks for the first
`pyproject.toml`, `ruff.toml`, or `.ruff.toml` file in the file's directory or any parent directory.
Let's create a `pyproject.toml` file in our project's root directory:
To configure Ruff, let's create a `pyproject.toml` file in our project's root directory:
```toml
[tool.ruff]
# Set the maximum line length to 79 characters.
line-length = 79
[tool.ruff.linter]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule.
extend-select = ["E501"]
# Set the maximum line length to 79 characters.
line-length = 79
```
Running Ruff again, we can see that it now enforces a line length of 79 characters:
Running Ruff again, we see that it now enforces a maximum line width, with a limit of 79 characters:
```shell
ruff check .
@ -102,10 +134,12 @@ specifically, we'll want to make note of the minimum supported Python version:
requires-python = ">=3.10"
[tool.ruff]
# Add the `line-too-long` rule to the enforced rule set.
extend-select = ["E501"]
# Set the maximum line length to 79 characters.
line-length = 79
[tool.ruff.linter]
# Add the `line-too-long` rule to the enforced rule set.
extend-select = ["E501"]
```
### Rule Selection
@ -115,7 +149,7 @@ determining the right set of rules will depend on your project's needs: some rul
strict, some are framework-specific, and so on.
By default, Ruff enables Flake8's `F` rules, along with a subset of the `E` rules, omitting any
stylistic rules that overlap with the use of a formatter, like
stylistic rules that overlap with the use of a formatter, like `ruff format` or
[Black](https://github.com/psf/black).
If you're introducing a linter for the first time, **the default rule set is a great place to
@ -130,7 +164,7 @@ rules, we can set our `pyproject.toml` to the following:
[project]
requires-python = ">=3.10"
[tool.ruff]
[tool.ruff.linter]
extend-select = [
"UP", # pyupgrade
]
@ -153,13 +187,13 @@ all functions have docstrings:
[project]
requires-python = ">=3.10"
[tool.ruff]
[tool.ruff.linter]
extend-select = [
"UP", # pyupgrade
"D", # pydocstyle
]
[tool.ruff.pydocstyle]
[tool.ruff.linter.pydocstyle]
convention = "google"
```
@ -188,7 +222,7 @@ def sum_even_numbers(numbers: Iterable[int]) -> int:
return sum(num for num in numbers if num % 2 == 0)
```
Running Ruff again, we'll see that it no longer flags the `Iterable` import:
Running `ruff check` again, we'll see that it no longer flags the `Iterable` import:
```shell
ruff check .
@ -210,7 +244,7 @@ def sum_even_numbers(numbers: Iterable[int]) -> int:
return sum(num for num in numbers if num % 2 == 0)
```
For more in-depth instructions on ignoring errors, see [_Configuration_](configuration.md#error-suppression).
For more in-depth instructions on ignoring errors, please see [_Error suppression_](linter.md#error-suppression).
### Adding Rules
@ -241,24 +275,27 @@ index b9291c5ca..b9f15b8c1 100644
def sum_even_numbers(numbers: Iterable[int]) -> int:
```
## Continuous Integration
## Integrations
This tutorial has focused on Ruff's command-line interface, but Ruff can also be used as a
[pre-commit](https://pre-commit.com) hook:
[pre-commit](https://pre-commit.com) hook via [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit):
```yaml
# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.1
hooks:
- id: ruff
# Run the Ruff formatter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.291
hooks:
- id: ruff-format
```
See [_Usage_](usage.md) for more.
## Editor Integrations
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or
alongside any other editor through the [Ruff LSP](https://github.com/astral-sh/ruff-lsp).
See [_Editor Integrations_](editor-integrations.md).
For more, see [_Integrations_](integrations.md).

View file

@ -1,109 +0,0 @@
# Using Ruff
To run Ruff, try any of the following:
```shell
ruff check . # Lint all files in the current directory (and any subdirectories)
ruff check path/to/code/ # Lint all files in `/path/to/code` (and any subdirectories)
ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code`
ruff check path/to/code/to/file.py # Lint `file.py`
ruff check @file_paths.txt # Lint using an input file and treat its contents as command-line arguments (newline delimiter)
```
You can run Ruff in `--watch` mode to automatically re-run on-change:
```shell
ruff check path/to/code/ --watch
```
## pre-commit
Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.1
hooks:
- id: ruff
```
Or, to enable fixes:
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.1
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
```
Or, to run the hook on Jupyter Notebooks too:
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.1
hooks:
- id: ruff
types_or: [python, pyi, jupyter]
```
Ruff's pre-commit hook should be placed after other formatting tools, such as Black and isort,
_unless_ you enable fixes, in which case, Ruff's pre-commit hook should run _before_ Black, isort,
and other formatting tools, as Ruff's fix behavior can output code changes that require
reformatting.
## VS Code
Ruff can also be used as a [VS Code extension](https://github.com/astral-sh/ruff-vscode) or
alongside any other editor through the [Ruff LSP](https://github.com/astral-sh/ruff-lsp).
## GitHub Action
Ruff can also be used as a GitHub Action via [`ruff-action`](https://github.com/chartboost/ruff-action).
By default, `ruff-action` runs as a pass-fail test to ensure that a given repository doesn't contain
any lint rule violations as per its [configuration](https://github.com/astral-sh/ruff/blob/main/docs/configuration.md).
However, under-the-hood, `ruff-action` installs and runs `ruff` directly, so it can be used to
execute any supported `ruff` command (e.g., `ruff check --fix`).
`ruff-action` supports all GitHub-hosted runners, and can be used with any published Ruff version
(i.e., any version available on [PyPI](https://pypi.org/project/ruff/)).
To use `ruff-action`, create a file (e.g., `.github/workflows/ruff.yml`) inside your repository
with:
```yaml
name: Ruff
on: [ push, pull_request ]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: chartboost/ruff-action@v1
```
Alternatively, you can include `ruff-action` as a step in any other workflow file:
```yaml
- uses: chartboost/ruff-action@v1
```
`ruff-action` accepts optional configuration parameters via `with:`, including:
- `version`: The Ruff version to install (default: latest).
- `options`: The command-line arguments to pass to Ruff (default: `"check"`).
- `src`: The source paths to pass to Ruff (default: `"."`).
For example, to run `ruff check --select B ./src` using Ruff version `0.0.259`:
```yaml
- uses: chartboost/ruff-action@v1
with:
src: "./src"
version: 0.0.259
args: --select B
```

View file

@ -61,6 +61,7 @@ extra_css:
- stylesheets/extra.css
not_in_nav: |
/rules/*
/formatter/*
extra:
analytics:
provider: fathom

View file

@ -22,14 +22,15 @@ class Section(NamedTuple):
SECTIONS: list[Section] = [
Section("Overview", "index.md", generated=True),
Section("Tutorial", "tutorial.md", generated=False),
Section("Installation", "installation.md", generated=False),
Section("Usage", "usage.md", generated=False),
Section("Configuration", "configuration.md", generated=False),
Section("Installing Ruff", "installation.md", generated=False),
Section("The Ruff Linter", "linter.md", generated=False),
Section("The Ruff Formatter", "formatter.md", generated=False),
Section("Configuring Ruff", "configuration.md", generated=False),
Section("Preview", "preview.md", generated=False),
Section("Rules", "rules.md", generated=True),
Section("Settings", "settings.md", generated=True),
Section("Editor Integrations", "editor-integrations.md", generated=False),
Section("Versioning", "versioning.md", generated=False),
Section("Integrations", "integrations.md", generated=False),
Section("FAQ", "faq.md", generated=False),
Section("Contributing", "contributing.md", generated=True),
]
@ -42,7 +43,7 @@ LINK_REWRITES: dict[str, str] = {
"configuration.md#pyprojecttoml-discovery"
),
"https://docs.astral.sh/ruff/contributing/": "contributing.md",
"https://docs.astral.sh/ruff/editor-integrations/": "editor-integrations.md",
"https://docs.astral.sh/ruff/integrations/": "integrations.md",
"https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8": (
"faq.md#how-does-ruff-compare-to-flake8"
),