1. Project Overview & Quickstart (sharkdp/LICENSE-MIT)
LICENSE-MIT
Open-source repository sharkdp/LICENSE-MIT
Repository Details
- Repository: sharkdp/LICENSE-MIT
- Primary Language: Code
Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.
2. Official Technical Reference & Guides (sharkdp/docs)
File: README.md
Astral Documentation
This repository contains documentation for the Astral toolchain: Ruff, uv, and ty.
License
uv is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in uv by you, as defined in the Apache-2.0 license, shall be
dually licensed as above, without any additional terms or conditions.
File: site/ruff/llms.txt
Ruff
An extremely fast Python linter and code formatter, written in Rust.
Ruff is an extremely fast Python linter and code formatter, written in Rust.
It can be used to replace Flake8 (plus dozens of plugins), Black, isort, pydocstyle,
pyupgrade, autoflake, and more, all while executing tens or hundreds of times faster
than any individual tool.
Ruff includes both a linter (ruff check) and a formatter (ruff format).
When fetching documentation, use explicit index.md paths for directories, e.g.,https://docs.astral.sh/ruff/editors/index.md. This returns clean markdown instead
of rendered HTML with JS/CSS.
Concepts
Configuration
Editors
Formatter
Getting started
Integrations
Linter
Reference
File: site/ruff/requirements.txt
PyYAML==6.0.3
ruff==0.15.15
mkdocs==1.6.1
mkdocs-material==9.7.6
mkdocs-redirects==1.2.3
mdformat==1.0.0
mdformat-mkdocs==5.1.4
mkdocs-github-admonitions-plugin @ git+https://github.com/PGijsbers/admonitions.git#7343d2f4a92e4d1491094530ef3d0d02d93afbb7
mkdocs-llmstxt==0.2.0
File: site/ruff/configuration/index.md
[Configuring Ruff](#configuring-ruff)
Ruff can be configured through a pyproject.toml, ruff.toml, or .ruff.toml file.
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.
If left unspecified, Ruff's default configuration is equivalent to:
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]
# Same as Black.
line-length = 88
indent-width = 4
# Assume Python 3.10
target-version = "py310"
[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]+?))$"
[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.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".ipynb_checkpoints",
".mypy_cache",
".nox",
".pants.d",
".pyenv",
".pytest_cache",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
".vscode",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"site-packages",
"venv",
]
# Same as Black.
line-length = 88
indent-width = 4
# Assume Python 3.10
target-version = "py310"
[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]+?))$"
[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.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
# Enable auto-formatting of code examples in docstrings. Markdown,
# reStructuredText code/literal blocks and doctests are all supported.
#
# This is currently disabled by default, but it is planned for this
# to be opt-out in the future.
docstring-code-format = false
# Set the line length limit used when formatting code snippets in
# docstrings.
#
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"As an example, the following would configure Ruff to:
[tool.ruff.lint]
# 1. Enable flake8-bugbear (`B`) rules, in addition to the defaults.
select = ["E4", "E7", "E9", "F", "B"]
# 2. Avoid enforcing line-length violations (`E501`)
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 selected subdirectories.
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["E402"]
"**/{tests,docs,tools}/*" = ["E402"]
[tool.ruff.format]
# 5. Use single quotes in `ruff format`.
quote-style = "single"[lint]
# 1. Enable flake8-bugbear (`B`) rules, in addition to the defaults.
select = ["E4", "E7", "E9", "F", "B"]
# 2. Avoid enforcing line-length violations (`E501`)
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 selected subdirectories.
[lint.per-file-ignores]
"__init__.py" = ["E402"]
"**/{tests,docs,tools}/*" = ["E402"]
[format]
# 5. Use single quotes in `ruff format`.
quote-style = "single"Linter plugin configurations are expressed as subsections, e.g.:
[tool.ruff.lint]
# Add "Q" to the list of enabled codes.
select = ["E4", "E7", "E9", "F", "Q"]
[tool.ruff.lint.flake8-quotes]
docstring-quotes = "double"[lint]
# Add "Q" to the list of enabled codes.
select = ["E4", "E7", "E9", "F", "Q"]
[lint.flake8-quotes]
docstring-quotes = "double"Ruff respects pyproject.toml, ruff.toml, and .ruff.toml files. All three implement an equivalent schema (though in the ruff.toml and .ruff.toml versions, the [tool.ruff] header and tool.ruff section prefix is omitted).
For a complete enumeration of the available configuration options, see Settings.
[Config file discovery](#config-file-discovery)
Similar to ESLint, Ruff supports hierarchical configuration, such that the "closest" config file in the directory hierarchy is used for every individual file, with all paths in the config file (e.g., exclude globs, src paths) being resolved relative to the directory containing that config file.
There are a few exceptions to these rules:
- In locating the "closest"
pyproject.tomlfile for a given path, Ruff ignores anypyproject.tomlfiles that lack a[tool.ruff]section. - 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 (likeexcludeglobs orsrcpaths) are resolved relative to the current working directory. - If no config 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 viaetcetera's base strategy, and all relative paths being again resolved relative to the current working directory. - Any config-file-supported settings that are provided on the command-line (e.g., via
--select) will override the settings in every resolved configuration file.
Unlike ESLint, 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 field, which allows you to inherit the settings from another config file, like so:
[tool.ruff]
# Extend the `pyproject.toml` file in the parent directory...
extend = "../pyproject.toml"
# ...but use a different line length.
line-length = 100# Extend the `ruff.toml` file in the parent directory...
extend = "../ruff.toml"
# ...but use a different line length.
line-length = 100All of the above rules apply equivalently to pyproject.toml, 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.
[Inferring the Python version](#inferring-the-python-version)
When no discovered configuration specifies a target-version, Ruff will attempt to fall back to the minimum version compatible with the requires-python field in a nearby pyproject.toml. The rules for this behavior are as follows:
- If a configuration file is passed directly, Ruff does not attempt to infer a missing
target-version. - If a configuration file is found in the filesystem hierarchy, Ruff will infer a missing
target-versionfrom therequires-pythonfield in apyproject.tomlfile in the same directory as the found configuration. - If we are using a user-level configuration from
${config_dir}/ruff/pyproject.toml, therequires-pythonfield in the firstpyproject.tomlfile found in an ancestor of the current working directory takes precedence over thetarget-versionin the user-level configuration. - If no configuration files are found, Ruff will infer the
target-versionfrom therequires-pythonfield in the firstpyproject.tomlfile found in an ancestor of the current working directory.
Note that in these last two cases, the behavior of Ruff may differ depending on the working directory from which it is invoked.
[Python file discovery](#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 and extend-exclude settings in each directory's configuration file.
Files can also be selectively excluded from linting or formatting by scoping the exclude setting 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:
[tool.ruff.format]
exclude = ["*.pyi"][format]
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).
Files that are passed to ruff directly are always analyzed, regardless of the above criteria, unless force-exclude is also enabled (via CLI or settings file). For example, without force-exclude enabled, ruff check /path/to/excluded/file.py will always lint file.py.
[Default inclusions](#default-inclusions)
By default, Ruff will discover files matching *.py, *.pyi, *.ipynb, or pyproject.toml. In preview mode, Ruff will also discover *.pyw by default.
To lint or format files with additional file extensions, use the extend-include setting. You can also change the default selection using the include setting.
[tool.ruff]
include = ["pyproject.toml", "src/**/*.py", "scripts/**/*.py"]include = ["pyproject.toml", "src/**/*.py", "scripts/**/*.py"]Warning
Paths provided to include must match files. For example, include = ["src"] will fail since it matches a directory.
[Jupyter Notebook discovery](#jupyter-notebook-discovery)
Ruff has built-in support for linting and formatting Jupyter Notebooks, which are linted and formatted by default on version 0.6.0 and higher.
If you'd prefer to either only lint or only format Jupyter Notebook files, you can use the section-specific exclude option to do so. For example, the following would only lint Jupyter Notebook files and not format them:
[tool.ruff.format]
exclude = ["*.ipynb"][format]
exclude = ["*.ipynb"]And, conversely, the following would only format Jupyter Notebook files and not lint them:
[tool.ruff.lint]
exclude = ["*.ipynb"][lint]
exclude = ["*.ipynb"]You can completely disable Jupyter Notebook support by updating the extend-exclude setting:
[tool.ruff]
extend-exclude = ["*.ipynb"]extend-exclude = ["*.ipynb"]If you'd like to ignore certain rules specifically for Jupyter Notebook files, you can do so by using the per-file-ignores setting:
[tool.ruff.lint.per-file-ignores]
"*.ipynb" = ["T20"][lint.per-file-ignores]
"*.ipynb" = ["T20"]Some rules have different behavior when applied to Jupyter Notebook files. For example, when applied to .py files the module-import-not-at-top-of-file (E402) rule detect imports at the top of a file, but for notebooks it detects imports at the top of a cell. For a given rule, the rule's documentation will always specify if it has different behavior when applied to Jupyter Notebook files.
[Command-line interface](#command-line-interface)
Some configuration options can be provided or overridden via dedicated flags on the command line. This includes those related to rule enablement and disablement, file discovery, logging level, and more:
$ ruff check path/to/code/ --select F401 --select F403 --quietAll other configuration options can be set via the command line using the --config flag, detailed below.
[The `--config` CLI flag](#the-config-cli-flag)
The --config flag has two uses. It is most often used to point to the configuration file that you would like Ruff to use, for example:
$ ruff check path/to/directory --config path/to/ruff.tomlHowever, the --config flag can also be used to provide arbitrary overrides of configuration settings using TOML <KEY> = <VALUE> pairs. This is mostly useful in situations where you wish to override a configuration setting that does not have a dedicated command-line flag.
In the below example, the --config flag is the only way of overriding the dummy-variable-rgx configuration setting from the command line, since this setting has no dedicated CLI flag. The per-file-ignores setting could also have been overridden via the --per-file-ignores dedicated flag, but using --config to override the setting is also fine:
$ ruff check path/to/file --config path/to/ruff.toml --config "lint.dummy-variable-rgx = '__.*'" --config "lint.per-file-ignores = {'some_file.py' = ['F841']}"Configuration options passed to --config are parsed in the same way as configuration options in a ruff.toml file. As such, options specific to the Ruff linter need to be prefixed with lint. (--config "lint.dummy-variable-rgx = '__.*'" rather than simply --config "dummy-variable-rgx = '__.*'"), and options specific to the Ruff formatter need to be prefixed with format..
If a specific configuration option is simultaneously overridden by a dedicated flag and by the --config flag, the dedicated flag takes priority. In this example, the maximum permitted line length will be set to 90, not 100:
$ ruff format path/to/file --line-length=90 --config "line-length=100"Specifying --config "line-length=90" will override the line-length setting from all configuration files detected by Ruff, including configuration files discovered in subdirectories. In this respect, specifying --config "line-length=90" has the same effect as specifying --line-length=90, which will similarly override the line-length setting from all configuration files detected by Ruff, regardless of where a specific configuration file is located.
[Full command-line interface](#full-command-line-interface)
See ruff help for the full list of Ruff's top-level commands:
Ruff: An extremely fast Python linter and code formatter.
Usage: ruff [OPTIONS] <COMMAND>
Commands:
check Run Ruff on the given files or directories
rule Explain a rule (or all rules)
config List or describe the available configuration options
linter List all supported upstream linters
clean Clear any caches in the current directory and any subdirectories
format Run the Ruff formatter on the given files or directories
server Run the language server
analyze Run analysis over Python source code
version Display Ruff's version
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help (see more with '--help')
-V, --version Print version
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)
Global options:
--config <CONFIG_OPTION>
Either a path to a TOML configuration file (`pyproject.toml` or
`ruff.toml`), or a TOML `<KEY> = <VALUE>` pair (such as you might
find in a `ruff.toml` configuration file) overriding a specific
configuration option (e.g., `--config "lint.line-length = 100"` or
`--config "format.quote-style = 'single'"`). Overrides of individual
settings using this option always take precedence over all
configuration files, including configuration files that were also
specified using `--config`
--isolated
Ignore all configuration files
--color <WHEN>
Control when colored output is used [possible values: auto, always,
never]
For help with a specific command, see: `ruff help <command>`.Or ruff help check for more on the linting command:
Run Ruff on the given files or directories
Usage: ruff check [OPTIONS] [FILES]...
Arguments:
[FILES]... List of files or directories to check, or `-` to read from stdin
[default: .]
Options:
--fix
Apply fixes to resolve lint violations. Use `--no-fix` to disable or
`--unsafe-fixes` to include unsafe fixes
--unsafe-fixes
Include fixes that may not retain the original intent of the code.
Use `--no-unsafe-fixes` to disable
--show-fixes
Show an enumeration of all fixed lint violations. Use
`--no-show-fixes` to disable
--diff
Avoid writing any fixed files back; instead, output a diff for each
changed file to stdout, and exit 0 if there are no diffs. Implies
`--fix-only`
-w, --watch
Run in watch mode by re-running whenever files change
--fix-only
Apply fixes to resolve lint violations, but don't report on, or exit
non-zero for, leftover violations. Implies `--fix`. Use
`--no-fix-only` to disable or `--unsafe-fixes` to include unsafe
fixes
--ignore-noqa
Ignore any `# noqa` comments
--output-format <OUTPUT_FORMAT>
Output serialization format for violations. The default serialization
format is "full" [env: RUFF_OUTPUT_FORMAT=] [possible values:
concise, full, json, json-lines, junit, grouped, github, gitlab,
pylint, rdjson, azure, sarif]
-o, --output-file <OUTPUT_FILE>
Specify file to write the linter output to (default: stdout) [env:
RUFF_OUTPUT_FILE=]
--target-version <TARGET_VERSION>
The minimum Python version that should be supported [possible values:
py37, py38, py39, py310, py311, py312, py313, py314, py315]
--preview
Enable preview mode; checks will include unstable rules and fixes.
Use `--no-preview` to disable
--extension <EXTENSION>
List of mappings from file extension to language (one of `python`,
`ipynb`, `pyi`). For example, to treat `.ipy` files as IPython
notebooks, use `--extension ipy:ipynb`
--statistics
Show counts for every rule with at least one violation
--add-noqa[=<REASON>]
Enable automatic additions of `noqa` directives to failing lines.
Optionally provide a reason to append after the codes
--show-files
See the files Ruff will be run against with the current settings
--show-settings
See the settings Ruff will use to lint a given Python file
-h, --help
Print help (see more with '--help')
Rule selection:
--select <RULE_CODE>
Comma-separated list of rule codes to enable (or ALL, to enable all
rules)
--ignore <RULE_CODE>
Comma-separated list of rule codes to disable
--extend-select <RULE_CODE>
Like --select, but adds additional rule codes on top of those already
specified
--per-file-ignores <PER_FILE_IGNORES>
List of mappings from file pattern to code to exclude
--extend-per-file-ignores <EXTEND_PER_FILE_IGNORES>
Like `--per-file-ignores`, but adds additional ignores on top of
those already specified
--fixable <RULE_CODE>
List of rule codes to treat as eligible for fix. Only applicable when
fix itself is enabled (e.g., via `--fix`)
--unfixable <RULE_CODE>
List of rule codes to treat as ineligible for fix. Only applicable
when fix itself is enabled (e.g., via `--fix`)
--extend-fixable <RULE_CODE>
Like --fixable, but adds additional rule codes on top of those
already specified
File selection:
--exclude <FILE_PATTERN>
List of paths, used to omit files and/or directories from analysis
--extend-exclude <FILE_PATTERN>
Like --exclude, but adds additional files and directories on top of
those already excluded
--respect-gitignore
Respect file exclusions via `.gitignore` and other standard ignore
files. Use `--no-respect-gitignore` to disable
--force-exclude
Enforce exclusions, even for paths passed to Ruff directly on the
command-line. Use `--no-force-exclude` to disable
Miscellaneous:
-n, --no-cache
Disable cache reads [env: RUFF_NO_CACHE=]
--cache-dir <CACHE_DIR>
Path to the cache directory [env: RUFF_CACHE_DIR=]
--stdin-filename <STDIN_FILENAME>
The name of the file when passing it through stdin
-e, --exit-zero
Exit with status code "0", even upon detecting lint violations
--exit-non-zero-on-fix
Exit with a non-zero status code if any files were modified via fix,
even if no lint violations remain
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)
Global options:
--config <CONFIG_OPTION>
Either a path to a TOML configuration file (`pyproject.toml` or
`ruff.toml`), or a TOML `<KEY> = <VALUE>` pair (such as you might
find in a `ruff.toml` configuration file) overriding a specific
configuration option (e.g., `--config "lint.line-length = 100"` or
`--config "format.quote-style = 'single'"`). Overrides of individual
settings using this option always take precedence over all
configuration files, including configuration files that were also
specified using `--config`
--isolated
Ignore all configuration files
--color <WHEN>
Control when colored output is used [possible values: auto, always,
never]Or ruff help format for more on the formatting command:
Run the Ruff formatter on the given files or directories
Usage: ruff format [OPTIONS] [FILES]...
Arguments:
[FILES]... List of files or directories to format, or `-` to read from stdin
[default: .]
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
--extension <EXTENSION>
List of mappings from file extension to language (one of `python`,
`ipynb`, `pyi`). For example, to treat `.ipy` files as IPython
notebooks, use `--extension ipy:ipynb`
--target-version <TARGET_VERSION>
The minimum Python version that should be supported [possible values:
py37, py38, py39, py310, py311, py312, py313, py314, py315]
--preview
Enable preview mode; enables unstable formatting. Use `--no-preview`
to disable
--output-format <OUTPUT_FORMAT>
Output serialization format for violations, when used with `--check`.
The default serialization format is "full" [env: RUFF_OUTPUT_FORMAT=]
[possible values: concise, full, json, json-lines, junit, grouped,
github, gitlab, pylint, rdjson, azure, sarif]
-h, --help
Print help (see more with '--help')
Miscellaneous:
-n, --no-cache
Disable cache reads [env: RUFF_NO_CACHE=]
--cache-dir <CACHE_DIR>
Path to the cache directory [env: RUFF_CACHE_DIR=]
--stdin-filename <STDIN_FILENAME>
The name of the file when passing it through stdin
--exit-non-zero-on-format
Exit with a non-zero status code if any files were modified via
format, even if all files were formatted successfully
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
Format configuration:
--line-length <LINE_LENGTH> Set the line-length
Editor options:
--range <RANGE> When specified, Ruff will try to only format the code in
the given range.
It might be necessary to extend the start backwards or
the end forwards, to fully enclose a logical line.
The `<RANGE>` uses the format
`<start_line>:<start_column>-<end_line>:<end_column>`.
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)
Global options:
--config <CONFIG_OPTION>
Either a path to a TOML configuration file (`pyproject.toml` or
`ruff.toml`), or a TOML `<KEY> = <VALUE>` pair (such as you might
find in a `ruff.toml` configuration file) overriding a specific
configuration option (e.g., `--config "lint.line-length = 100"` or
`--config "format.quote-style = 'single'"`). Overrides of individual
settings using this option always take precedence over all
configuration files, including configuration files that were also
specified using `--config`
--isolated
Ignore all configuration files
--color <WHEN>
Control when colored output is used [possible values: auto, always,
never][Shell autocompletion](#shell-autocompletion)
Ruff supports autocompletion for most shells. A shell-specific completion script can be generated by ruff generate-shell-completion <SHELL>, where <SHELL> is one of bash, elvish, fig, fish, powershell, or zsh.
Tip
You can run echo $SHELL to help you determine your shell.
To enable shell autocompletion for Ruff, run one of the following:
echo 'eval "$(ruff generate-shell-completion bash)"' >> ~/.bashrcecho 'eval "$(ruff generate-shell-completion zsh)"' >> ~/.zshrcecho 'ruff generate-shell-completion fish | source' > ~/.config/fish/completions/ruff.fishecho 'eval (ruff generate-shell-completion elvish | slurp)' >> ~/.elvish/rc.elvif (!(Test-Path -Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
Add-Content -Path $PROFILE -Value '(& ruff generate-shell-completion powershell) | Out-String | Invoke-Expression'Then restart the shell or source the shell config file.
File: site/ruff/editors/index.md
[Editor Integrations](#editor-integrations)
Ruff can be integrated with various editors and IDEs to provide a seamless development experience. This section provides instructions on how to set up Ruff with your editor and configure it to your liking.
[Language Server Protocol](#language-server-protocol)
The editor integration is mainly powered by the Ruff Language Server which implements the Language Server Protocol. The server is written in Rust and is available as part of the ruff CLI via ruff server. It is a single, common backend built directly into Ruff, and a direct replacement for ruff-lsp, our previous language server. You can read more about ruff server in the v0.4.5 blog post.
The server supports surfacing Ruff diagnostics, providing Code Actions to fix them, and formatting the code using Ruff's built-in formatter. Currently, the server is intended to be used alongside another Python Language Server in order to support features like navigation and autocompletion.
The Ruff Language Server was available first in Ruff v0.4.5 in beta and stabilized in Ruff v0.5.3.
Note
This is the documentation for Ruff's built-in language server written in Rust (ruff server). If you are looking for the documentation for the ruff-lsp language server, please refer to the README of the ruff-lsp repository.
File: site/ruff/editors/features/index.md
[Features](#features)
This section provides a detailed overview of the features provided by the Ruff Language Server.
[Diagnostic Highlighting](#diagnostic-highlighting)
Provide diagnostics for your Python code in real-time.
[Dynamic Configuration](#dynamic-configuration)
The server dynamically refreshes the diagnostics when a configuration file is changed in the workspace, whether it's a pyproject.toml, ruff.toml, or .ruff.toml file.
The server relies on the file watching capabilities of the editor to detect changes to these files. If an editor does not support file watching, the server will not be able to detect changes to the configuration file and thus will not refresh the diagnostics.
[Formatting](#formatting)
Provide code formatting for your Python code. The server can format an entire document or a specific range of lines.
The VS Code extension provides the Ruff: Format Document command to format an entire document. In VS Code, the range formatting can be triggered by selecting a range of lines, right-clicking, and selecting Format Selection from the context menu.
[Markdown code blocks](#markdown-code-blocks)
This feature is currently only available in preview mode.
The Ruff formatter can also format Python code blocks in Markdown files. The Ruff VS Code extension provides the Format Document command for Markdown files, which will then format the code blocks with the same settings as used for regular Python files.
Note that Ruff will not format any other parts of Markdown files. If you want to use Ruff formatting in addition to another Markdown formatting extension, then you will need to set one as the default in VS Code, and manually run the Format Document With... (or Ruff: Format document) command to run any other formatters separately.
To enable preview mode for formatting in VS Code, add the following to your settings.json:
{
"ruff.format.preview": true,
}To set Ruff as the default formatter for Markdown files in VS Code, add the following to your settings.json:
{
"[markdown]": {
"editor.defaultFormatter": "charliermarsh.ruff"
}
}Note that Ruff does not support range formatting for Markdown files, so if you have format-on-save enabled, you may also need to specify the following:
{
"[markdown]": {
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file"
}
}See the formatter documentation for more details.
[Code Actions](#code-actions)
Code actions are context-sensitive suggestions that can help you fix issues in your code. They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. The Ruff Language Server provides the following code actions:
- Apply a quick fix for a diagnostic that has a fix available (e.g., removing an unused import).
- Ignore a diagnostic with a
# noqacomment. - Apply all quick fixes available in the document.
- Organize imports in the document.
You can even run these actions on-save. For example, to fix all issues and organize imports on save in VS Code, add the following to your settings.json:
{
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
}
}[Fix Safety](#fix-safety)
Ruff's automatic fixes are labeled as "safe" and "unsafe". By default, the "Fix all" action will not apply unsafe fixes. However, unsafe fixes can be applied manually with the "Quick fix" action. Application of unsafe fixes when using "Fix all" can be enabled by setting unsafe-fixes = true in your Ruff configuration file.
See the Ruff fix documentation for more details on how fix safety works.
[Hover](#hover)
The server can provide the rule documentation when focusing over a NoQA code in the comment. Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
[Jupyter Notebook](#jupyter-notebook)
Similar to Ruff's CLI, the Ruff Language Server fully supports Jupyter Notebook files with all the capabilities available to Python files.
Note
Ruff has built-in support for Jupyter Notebooks. The native language server will discover and lint or format .ipynb files by default as of version 0.6.0. Refer to the Jupyter Notebook discovery section for more details.
File: site/ruff/editors/migration/index.md
[Migrating from `ruff-lsp`](#migrating-from-ruff-lsp)
ruff-lsp is the Language Server Protocol implementation for Ruff to power the editor integrations. It is written in Python and is a separate package from Ruff itself. The native server, however, is the Language Server Protocol implementation which is written in Rust and is available under the ruff server command. This guide is intended to help users migrate from ruff-lsp to the native server.
Note
The native server was first introduced in Ruff version 0.3.5. It was marked as beta in version 0.4.5 and officially stabilized in version 0.5.3. It is recommended to use the latest version of Ruff to ensure the best experience.
The migration process involves any or all of the following:
- Migrate deprecated settings to the new settings
- Remove settings that are no longer supported
- Update the
ruffversion
Read on to learn more about the unsupported or new settings, or jump to the examples that enumerate some of the common settings and how to migrate them.
[Unsupported Settings](#unsupported-settings)
The following ruff-lsp settings are not supported by the native server:
lint.run: This setting is no longer relevant for the native language server, which runs on every keystroke by default.lint.args,format.args: These settings have been replaced by more granular settings in the native server likelint.select,format.preview, etc. along with the ability to override any configuration using theconfigurationsetting.
The following settings are not accepted by the language server but are still used by the VS Code extension. Refer to their respective documentation for more information on how each is used by the extension:
[Removed Settings](#removed-settings)
Additionally, the following settings are not supported by the native server and should be removed:
[New Settings](#new-settings)
The native server introduces several new settings that ruff-lsp does not have:
configurationconfigurationPreferenceexcludeformat.previewlineLengthlint.selectlint.extendSelectlint.ignorelint.preview
[Examples](#examples)
All of the examples mentioned below are only valid for the VS Code extension. For other editors, please refer to their respective documentation sections in the settings page.
[Configuration file](#configuration-file)
If you've been providing a configuration file as shown below:
{
"ruff.lint.args": "--config ~/.config/custom_ruff_config.toml",
"ruff.format.args": "--config ~/.config/custom_ruff_config.toml"
}You can migrate to the new server by using the configuration setting like below which will apply the configuration to both the linter and the formatter:
{
"ruff.configuration": "~/.config/custom_ruff_config.toml"
}[`lint.args`](#lintargs)
If you're providing the linter flags by using ruff.lint.args like so:
{
"ruff.lint.args": "--select=E,F --unfixable=F401 --unsafe-fixes"
}You can migrate to the new server by using the lint.select and configuration setting like so:
{
"ruff.lint.select": ["E", "F"],
"ruff.configuration": {
"unsafe-fixes": true,
"lint": {
"unfixable": ["F401"]
}
}
}The following options can be set directly in the editor settings:
The remaining options can be set using the configuration setting.
[`format.args`](#formatargs)
If you're also providing formatter flags by using ruff.format.args like so:
{
"ruff.format.args": "--line-length 80 --config='format.quote-style=double'"
}You can migrate to the new server by using the lineLength and configuration setting like so:
{
"ruff.lineLength": 80,
"ruff.configuration": {
"format": {
"quote-style": "double"
}
}
}The following options can be set directly in the editor settings:
The remaining options can be set using the configuration setting.
File: site/ruff/editors/settings/index.md
[Settings](#settings)
The Ruff Language Server provides a set of configuration options to customize its behavior along with the ability to use an existing pyproject.toml or ruff.toml file to configure the linter and formatter. This is done by providing these settings while initializing the server. VS Code provides a UI to configure these settings, while other editors may require manual configuration. The setup section provides instructions on where to place these settings as per the editor.
[Top-level](#top-level)
[`configuration`](#configuration)
The configuration setting allows you to configure editor-specific Ruff behavior. This can be done in one of the following ways:
- Configuration file path: Specify the path to a
ruff.tomlorpyproject.tomlfile that contains the configuration. User home directory and environment variables will be expanded. - Inline JSON configuration: Directly provide the configuration as a JSON object.
Added in Ruff 0.9.8
The Inline JSON configuration option was introduced in Ruff 0.9.8.
The default behavior, if configuration is unset, is to load the settings from the project's configuration (a ruff.toml or pyproject.toml in the project's directory), consistent with when running Ruff on the command-line.
The configurationPreference setting controls the precedence if both an editor-provided configuration (configuration) and a project level configuration file are present.
[Resolution order](#configuration_resolution_order)
In an editor, Ruff supports three sources of configuration, prioritized as follows (from highest to lowest):
- Specific settings: Individual settings like
lineLengthorlint.selectdefined in the editor ruff.configuration: Settings provided via theconfigurationfield (either a path to a configuration file or an inline configuration object)- Configuration file: Settings defined in a
ruff.tomlorpyproject.tomlfile in the project's directory (if present)
For example, if the line length is specified in all three sources, Ruff will use the value from the lineLength setting.
Default value: null
Type: string
Example usage:
Using configuration file path:
{
"ruff.configuration": "~/path/to/ruff.toml"
}vim.lsp.config('ruff', {
init_options = {
settings = {
configuration = "~/path/to/ruff.toml"
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"configuration": "~/path/to/ruff.toml"
}
}
}
}
}Using inline configuration:
{
"ruff.configuration": {
"lint": {
"unfixable": ["F401"],
"extend-select": ["TID251"],
"flake8-tidy-imports": {
"banned-api": {
"typing.TypedDict": {
"msg": "Use `typing_extensions.TypedDict` instead",
}
}
}
},
"format": {
"quote-style": "single"
}
}
}vim.lsp.config('ruff', {
init_options = {
settings = {
configuration = {
lint = {
unfixable = {"F401"},
["extend-select"] = {"TID251"},
["flake8-tidy-imports"] = {
["banned-api"] = {
["typing.TypedDict"] = {
msg = "Use `typing_extensions.TypedDict` instead"
}
}
}
},
format = {
["quote-style"] = "single"
}
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"configuration": {
"lint": {
"unfixable": ["F401"],
"extend-select": ["TID251"],
"flake8-tidy-imports": {
"banned-api": {
"typing.TypedDict": {
"msg": "Use `typing_extensions.TypedDict` instead"
}
}
}
},
"format": {
"quote-style": "single"
}
}
}
}
}
}
}[`configurationPreference`](#configurationpreference)
The strategy to use when resolving settings across VS Code and the filesystem. By default, editor configuration is prioritized over ruff.toml and pyproject.toml files.
"editorFirst": Editor settings take priority over configuration files present in the workspace."filesystemFirst": Configuration files present in the workspace takes priority over editor settings."editorOnly": Ignore configuration files entirely i.e., only use editor settings.
Default value: "editorFirst"
Type: "editorFirst" | "filesystemFirst" | "editorOnly"
Example usage:
{
"ruff.configurationPreference": "filesystemFirst"
}vim.lsp.config('ruff', {
init_options = {
settings = {
configurationPreference = "filesystemFirst"
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"configurationPreference": "filesystemFirst"
}
}
}
}
}[`exclude`](#exclude)
A list of file patterns to exclude from linting and formatting. See the documentation for more details.
Default value: null
Type: string[]
Example usage:
{
"ruff.exclude": ["**/tests/**"]
}vim.lsp.config('ruff', {
init_options = {
settings = {
exclude = ["**/tests/**"]
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"exclude": ["**/tests/**"]
}
}
}
}
}[`lineLength`](#linelength)
The line length to use for the linter and formatter.
Default value: null
Type: int
Example usage:
{
"ruff.lineLength": 100
}vim.lsp.config('ruff', {
init_options = {
settings = {
lineLength = 100
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"lineLength": 100
}
}
}
}
}[`fixAll`](#fixall)
Whether to register the server as capable of handling source.fixAll code actions.
Default value: true
Type: bool
Example usage:
{
"ruff.fixAll": false
}vim.lsp.config('ruff', {
init_options = {
settings = {
fixAll = false
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"fixAll": false
}
}
}
}
}[`organizeImports`](#organizeimports)
Whether to register the server as capable of handling source.organizeImports code actions.
Default value: true
Type: bool
Example usage:
{
"ruff.organizeImports": false
}vim.lsp.config('ruff', {
init_options = {
settings = {
organizeImports = false
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"organizeImports": false
}
}
}
}
}[`showSyntaxErrors`](#showsyntaxerrors)
New in Ruff v0.5.0
Whether to show syntax error diagnostics.
Default value: true
Type: bool
Example usage:
{
"ruff.showSyntaxErrors": false
}vim.lsp.config('ruff', {
init_options = {
settings = {
showSyntaxErrors = false
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"showSyntaxErrors": false
}
}
}
}
}[`logLevel`](#loglevel)
The log level to use for the server.
Default value: "info"
Type: "trace" | "debug" | "info" | "warn" | "error"
Example usage:
{
"ruff.logLevel": "debug"
}vim.lsp.config('ruff', {
init_options = {
settings = {
logLevel = "debug"
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"logLevel": "debug"
}
}
}
}
}[`logFile`](#logfile)
Path to the log file to use for the server.
If not set, logs will be written to stderr.
Default value: null
Type: string
Example usage:
{
"ruff.logFile": "~/path/to/ruff.log"
}vim.lsp.config('ruff', {
init_options = {
settings = {
logFile = "~/path/to/ruff.log"
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"logFile": "~/path/to/ruff.log"
}
}
}
}
}[`codeAction`](#codeaction)
Enable or disable code actions provided by the server.
[`disableRuleComment.enable`](#disablerulecommentenable)
Whether to display Quick Fix actions to disable rules via noqa suppression comments.
Default value: true
Type: bool
Example usage:
{
"ruff.codeAction.disableRuleComment.enable": false
}vim.lsp.config('ruff', {
init_options = {
settings = {
codeAction = {
disableRuleComment = {
enable = false
}
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"codeAction": {
"disableRuleComment": {
"enable": false
}
}
}
}
}
}
}[`fixViolation.enable`](#fixviolationenable)
Whether to display Quick Fix actions to autofix violations.
Default value: true
Type: bool
Example usage:
{
"ruff.codeAction.fixViolation.enable": false
}vim.lsp.config('ruff', {
init_options = {
settings = {
codeAction = {
fixViolation = {
enable = false
}
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"codeAction": {
"fixViolation": {
"enable": false
}
}
}
}
}
}
}[`lint`](#lint)
Settings specific to the Ruff linter.
[`enable`](#lint_enable)
Whether to enable linting. Set to false to use Ruff exclusively as a formatter.
Default value: true
Type: bool
Example usage:
{
"ruff.lint.enable": false
}vim.lsp.config('ruff', {
init_options = {
settings = {
lint = {
enable = false
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"lint": {
"enable": false
}
}
}
}
}
}[`preview`](#lint_preview)
Whether to enable Ruff's preview mode when linting.
Default value: null
Type: bool
Example usage:
{
"ruff.lint.preview": true
}vim.lsp.config('ruff', {
init_options = {
settings = {
lint = {
preview = true
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"lint": {
"preview": true
}
}
}
}
}
}[`select`](#select)
Rules to enable by default. See the documentation.
Default value: null
Type: string[]
Example usage:
{
"ruff.lint.select": ["E", "F"]
}vim.lsp.config('ruff', {
init_options = {
settings = {
lint = {
select = {"E", "F"}
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"lint": {
"select": ["E", "F"]
}
}
}
}
}
}[`extendSelect`](#extendselect)
Rules to enable in addition to those in lint.select.
Default value: null
Type: string[]
Example usage:
{
"ruff.lint.extendSelect": ["W"]
}vim.lsp.config('ruff', {
init_options = {
settings = {
lint = {
extendSelect = {"W"}
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"lint": {
"extendSelect": ["W"]
}
}
}
}
}
}[`ignore`](#ignore)
Rules to disable by default. See the documentation.
Default value: null
Type: string[]
Example usage:
{
"ruff.lint.ignore": ["E4", "E7"]
}vim.lsp.config('ruff', {
init_options = {
settings = {
lint = {
ignore = {"E4", "E7"}
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"lint": {
"ignore": ["E4", "E7"]
}
}
}
}
}
}[`format`](#format)
Settings specific to the Ruff formatter.
[`preview`](#format_preview)
Whether to enable Ruff's preview mode when formatting.
Default value: null
Type: bool
Example usage:
{
"ruff.format.preview": true
}vim.lsp.config('ruff', {
init_options = {
settings = {
format = {
preview = true
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"format": {
"preview": true
}
}
}
}
}
}[`backend`](#format_backend)
The backend to use for formatting files. Following options are available:
"internal": Use the built-in Ruff formatter"uv": Use uv for formatting (requires uv >= 0.8.13)
For internal, the formatter version will match the selected Ruff version while for uv, the formatter version may differ.
Default value: "internal"
Type: "internal" | "uv"
Example usage:
{
"ruff.format.backend": "uv"
}vim.lsp.config('ruff', {
init_options = {
settings = {
format = {
backend = "uv"
}
}
}
}){
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
"format": {
"backend": "uv"
}
}
}
}
}
}[VS Code specific](#vs-code-specific)
Additionally, the Ruff extension provides the following settings specific to VS Code. These settings are not used by the language server and are only relevant to the extension.
[`enable`](#enable)
Whether to enable the Ruff extension. Modifying this setting requires restarting VS Code to take effect.
Default value: true
Type: bool
Example usage:
{
"ruff.enable": false
}[`format.args`](#formatargs)
Deprecated
This setting is only used by ruff-lsp which is deprecated in favor of the native language server. Refer to the migration guide for more information.
This setting is not used by the native language server.
Additional arguments to pass to the Ruff formatter.
Default value: []
Type: string[]
Example usage:
{
"ruff.format.args": ["--line-length", "100"]
}[`ignoreStandardLibrary`](#ignorestandardlibrary)
Deprecated
This setting is only used by ruff-lsp which is deprecated in favor of the native language server. Refer to the migration guide for more information.
This setting is not used by the native language server.
Whether to ignore files that are inferred to be part of the Python standard library.
Default value: true
Type: bool
Example usage:
{
"ruff.ignoreStandardLibrary": false
}[`importStrategy`](#importstrategy)
Strategy for loading the ruff executable.
fromEnvironmentfinds Ruff in the environment, falling back to the bundled versionuseBundleduses the version bundled with the extension
Default value: "fromEnvironment"
Type: "fromEnvironment" | "useBundled"
Example usage:
{
"ruff.importStrategy": "useBundled"
}[`interpreter`](#interpreter)
A list of paths to Python interpreters. Even though this is a list, only the first interpreter is used.
This setting depends on the ruff.nativeServer setting:
- If using the native server, the interpreter is used to find the
ruffexecutable whenruff.importStrategyis set tofromEnvironment. - Otherwise, the interpreter is used to run the
ruff-lspserver.
Default value: []
Type: string[]
Example usage:
{
"ruff.interpreter": ["/home/user/.local/bin/python"]
}[`lint.args`](#lintargs)
Deprecated
This setting is only used by ruff-lsp which is deprecated in favor of the native language server. Refer to the migration guide for more information.
This setting is not used by the native language server.
Additional arguments to pass to the Ruff linter.
Default value: []
Type: string[]
Example usage:
{
"ruff.lint.args": ["--config", "/path/to/pyproject.toml"]
}[`lint.run`](#lintrun)
Deprecated
This setting is only used by ruff-lsp which is deprecated in favor of the native language server. Refer to the migration guide for more information.
This setting is not used by the native language server.
Run Ruff on every keystroke (onType) or on save (onSave).
Default value: "onType"
Type: "onType" | "onSave"
Example usage:
{
"ruff.lint.run": "onSave"
}[`nativeServer`](#nativeserver)
Whether to use the native language server, ruff-lsp or automatically decide between the two based on the Ruff version and extension settings.
"on": Use the native language server. A warning will be displayed if deprecated settings are detected."off": Useruff-lsp. A warning will be displayed if settings specific to the native server are detected."auto": Automatically select between the native language server andruff-lspbased on the following conditions:true: Same asonfalse: Same asoff
Default value: "auto"
Type: "on" | "off" | "auto" | true | false
Example usage:
{
"ruff.nativeServer": "on"
}[`path`](#path)
A list of path to ruff executables.
The first executable in the list which is exists is used. This setting takes precedence over the ruff.importStrategy setting.
Default value: []
Type: string[]
Example usage:
{
"ruff.path": ["/home/user/.local/bin/ruff"]
}[`showNotifications`](#shownotifications)
Deprecated
This setting is only used by ruff-lsp which is deprecated in favor of the native language server. Refer to the migration guide for more information.
Setting to control when a notification is shown.
Default value: "off"
Type: "off" | "onError" | "onWarning" | "always"
Example usage:
{
"ruff.showNotifications": "onWarning"
}[`trace.server`](#traceserver)
The trace level for the language server. Refer to the LSP specification for more information.
Default value: "off"
Type: "off" | "messages" | "verbose"
Example usage:
{
"ruff.trace.server": "messages"
}File: site/ruff/editors/setup/index.md
[Setup](#setup)
We have specific setup instructions depending on your editor of choice. If you don't see your editor on this list and would like a setup guide, please open an issue.
If you're transferring your configuration from ruff-lsp, regardless of editor, there are several settings which have changed or are no longer available. See the migration guide for more.
Note
The setup instructions provided below are on a best-effort basis. If you encounter any issues while setting up the Ruff in an editor, please open an issue for assistance and help in improving this documentation.
Tip
Regardless of the editor, it is recommended to disable the older language server (ruff-lsp) to prevent any conflicts.
[VS Code](#vs-code)
Install the Ruff extension from the VS Code Marketplace. It is recommended to have the Ruff extension version 2024.32.0 or later to get the best experience with the Ruff Language Server.
For more documentation on the Ruff extension, refer to the README of the extension repository.
[Neovim](#neovim)
The Ruff language server can be setup in Neovim using the built-in LSP client either via vim.lsp.config (Neovim 0.11+) or the nvim-lspconfig plugin (Neovim 0.10 and earlier).
To setup using vim.lsp.config, you can either use the default configuration provided in nvim-lspconfig or configure the server without any external dependencies.
The following configuration needs to be stored in nvim/lsp/ruff.lua or nvim/after/lsp/ruff.lua:
---@type vim.lsp.Config
return {
cmd = { 'ruff', 'server' },
filetypes = { 'python' },
root_markers = { 'pyproject.toml', 'ruff.toml', '.ruff.toml', '.git' },
init_options = {
settings = {
-- Ruff language server settings go here
}
}
}And, then enable the server by including the following in your init.lua:
vim.lsp.enable('ruff')vim.lsp.config('ruff', {
init_options = {
settings = {
-- Ruff language server settings go here
}
}
})
vim.lsp.enable('ruff')Note
nvim-lspconfig has deprecated support for Neovim 0.10 and earlier in favor of using vim.lsp.config instead.
require('lspconfig').ruff.setup({
init_options = {
settings = {
-- Ruff language server settings go here
}
}
})Note
If the installed version of nvim-lspconfig includes the changes from neovim/nvim-lspconfig@70d1c2c, you will need to use Ruff version 0.5.3 or later.
If you're using Ruff alongside another language server (like Pyright), you may want to defer to that language server for certain capabilities, like textDocument/hover:
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup('lsp_attach_disable_ruff_hover', { clear = true }),
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client == nil then
return
end
if client.name == 'ruff' then
-- Disable hover in favor of Pyright
client.server_capabilities.hoverProvider = false
end
end,
desc = 'LSP: Disable hover capability from Ruff',
})If you'd like to use Ruff exclusively for linting, formatting, and organizing imports, you can disable those capabilities for Pyright:
vim.lsp.config('pyright', {
settings = {
pyright = {
-- Using Ruff's import organizer
disableOrganizeImports = true,
},
python = {
analysis = {
-- Ignore all files for analysis to exclusively use Ruff for linting
ignore = { '*' },
},
},
},
})By default, the log level for Ruff is set to info. To change the log level, you can set the logLevel setting:
vim.lsp.config('ruff', {
init_options = {
settings = {
logLevel = 'debug',
}
}
})By default, Ruff will write logs to stderr which will be available in Neovim's LSP client log file (:lua vim.print(vim.lsp.get_log_path())). It's also possible to divert these logs to a separate file with the logFile setting.
To view the trace logs between Neovim and Ruff, set the log level for Neovim's LSP client to debug:
vim.lsp.log.set_level('debug')vim.lsp.set_log_level('debug')With the conform.nvim plugin for Neovim.
require("conform").setup({
formatters_by_ft = {
python = {
-- To fix auto-fixable lint errors.
"ruff_fix",
-- To run the Ruff formatter.
"ruff_format",
-- To organize the imports.
"ruff_organize_imports",
},
},
})With the nvim-lint plugin for Neovim.
require("lint").linters_by_ft = {
python = { "ruff" },
}With the ALE plugin for Neovim or Vim. Neovim (using Lua):
-- Linters
vim.g.ale_linters = { python = { "ruff" } }
-- Fixers
vim.g.ale_fixers = { python = { "ruff", "ruff_format" } }Vim (using Vimscript):
" Linters
let g:ale_linters = { "python": ["ruff"] }
" Fixers
let g:ale_fixers = { "python": ["ruff", "ruff_format"] }For the fixers, ruff will run ruff check --fix (to fix all auto-fixable problems) whereas ruff_format will run ruff format.
[Vim](#vim)
The vim-lsp plugin can be used to configure the Ruff Language Server in Vim. To set it up, install vim-lsp plugin and register the server using the following in your .vimrc:
if executable('ruff')
au User lsp_setup call lsp#register_server({
\ 'name': 'ruff',
\ 'cmd': {server_info->['ruff', 'server']},
\ 'allowlist': ['python'],
\ 'workspace_config': {},
\ })
endifSee the vim-lsp documentation for more details on how to configure the language server.
If you're using Ruff alongside another LSP (like Pyright), you may want to defer to that LSP for certain capabilities, like textDocument/hover by adding the following to the function s:on_lsp_buffer_enabled():
function! s:on_lsp_buffer_enabled() abort
" add your keybindings here (see https://github.com/prabirshrestha/vim-lsp?tab=readme-ov-file#registering-servers)
let l:capabilities = lsp#get_server_capabilities('ruff')
if !empty(l:capabilities)
let l:capabilities.hoverProvider = v:false
endif
endfunctionRuff is also available as part of the coc-pyright extension for coc.nvim.
Ruff can also be integrated via efm language server in just a few lines. Following is an example config for efm to use Ruff for linting and formatting Python files:
tools:
python-ruff:
lint-command: "ruff check --stdin-filename ${INPUT} --output-format concise --quiet -"
lint-stdin: true
lint-formats:
- "%f:%l:%c: %m"
format-command: "ruff format --stdin-filename ${INPUT} --quiet -"
format-stdin: true[Helix](#helix)
Open the language configuration file for Helix and add the language server as follows:
[language-server.ruff]
command = "ruff"
args = ["server"]Then, you'll register the language server as the one to use with Python. If you don't already have a language server registered to use with Python, add this to languages.toml:
[[language]]
name = "python"
language-servers = ["ruff"]Otherwise, if you already have language-servers defined, you can simply add "ruff" to the list. For example, if you already have pylsp as a language server, you can modify the language entry as follows:
[[language]]
name = "python"
language-servers = ["ruff", "pylsp"]Note
Support for multiple language servers for a language is only available in Helix version 23.10 and later.
If you want to, as an example, turn on auto-formatting, add auto-format = true:
[[language]]
name = "python"
language-servers = ["ruff", "pylsp"]
auto-format = trueSee the Helix documentation for more settings you can use here.
You can pass settings into ruff server using [language-server.ruff.config.settings]. For example:
[language-server.ruff.config.settings]
lineLength = 80
[language-server.ruff.config.settings.lint]
select = ["E4", "E7"]
preview = false
[language-server.ruff.config.settings.format]
preview = trueBy default, the log level for Ruff is set to info. To change the log level, you can set the logLevel setting:
[language-server.ruff]
command = "ruff"
args = ["server"]
[language-server.ruff.config.settings]
logLevel = "debug"You can also divert Ruff's logs to a separate file with the logFile setting.
To view the trace logs between Helix and Ruff, pass in the -v (verbose) flag when starting Helix:
hx -v path/to/file.py[Kate](#kate)
- Activate the LSP Client plugin.
- Setup LSP Client as desired.
- Finally, add this to
Settings->Configure Kate->LSP Client->User Server Settings:
{
"servers": {
"python": {
"command": ["ruff", "server"],
"url": "https://github.com/astral-sh/ruff",
"highlightingModeRegex": "^Python$",
"settings": {}
}
}
}See LSP Client documentation for more details on how to configure the server from there.
Important
Kate's LSP Client plugin does not support multiple servers for the same language. As a workaround, you can use the python-lsp-server along with the python-lsp-ruff plugin to use Ruff alongside another language server. Note that this setup won't use the server settings because the python-lsp-ruff plugin uses the ruff executable and not the language server.
[Sublime Text](#sublime-text)
To use Ruff with Sublime Text, install Sublime Text's LSP and LSP-ruff package.
[PyCharm](#pycharm)
Starting with version 2025.3, PyCharm supports Ruff out of the box:
Go to Python | Tools | Ruff in the Settings dialog.
Select the Enable checkbox.
In the Execution mode setting, select how PyCharm should search for the executable:
Interpreter mode: PyCharm searches for an executable installed in your interpreter. To install the Ruff package for the selected interpreter, click Install Ruff.
Path mode: PyCharm searches for an executable in
$PATH. If the executable is not found, you can specify the path by clicking the Browse... icon.Select which options should be enabled.
For more information, refer to PyCharm documentation.
[Via External Tool](#via-external-tool)
Ruff can be installed as an External Tool in PyCharm. Open the Preferences pane, then navigate to "Tools", then "External Tools". From there, add a new tool with the following configuration:
Ruff should then appear as a runnable action:
[Via third-party plugin](#via-third-party-plugin)
Ruff is also available as the Ruff plugin on the IntelliJ Marketplace (maintained by @koxudaxi).
[Emacs](#emacs)
Ruff can be utilized as a language server via Eglot, which is in Emacs's core. To enable Ruff with automatic formatting on save, use the following configuration:
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(python-base-mode . ("ruff" "server"))))
(add-hook 'python-base-mode-hook
(lambda ()
(eglot-ensure)
(add-hook 'after-save-hook 'eglot-format nil t)))Ruff is available as flymake-ruff on MELPA:
(require 'flymake-ruff)
(add-hook 'python-mode-hook #'flymake-ruff-load)Ruff is also available as emacs-ruff-format:
(require 'ruff-format)
(add-hook 'python-mode-hook 'ruff-format-on-save-mode)Alternatively, it can be used via the Apheleia formatter library, by setting this configuration:
;; Replace default (black) to use ruff for sorting import and formatting.
(setf (alist-get 'python-mode apheleia-mode-alist)
'(ruff-isort ruff))
(setf (alist-get 'python-ts-mode apheleia-mode-alist)
'(ruff-isort ruff))[TextMate](#textmate)
Ruff is also available via the textmate2-ruff-linter bundle for TextMate.
[Zed](#zed)
Ruff support is now built into Zed (no separate extension required).
By default, Zed uses Ruff for formatting and linting.
To set up editor-wide Ruff options, provide the server settings under the lsp.ruff.initialization_options.settings key of your settings.json file:
{
"lsp": {
"ruff": {
"initialization_options": {
"settings": {
// Ruff server settings go here
"lineLength": 80,
"lint": {
"extendSelect": ["I"],
}
}
}
}
}
}format_on_save is enabled by default. You can disable it for Python by changing format_on_save in your settings.json file:
{
"languages": {
"Python": {
"format_on_save": "off"
}
}
}You can configure Ruff to fix lint violations and/or organize imports on-save by enabling the source.fixAll.ruff and source.organizeImports.ruff code actions respectively:
{
"languages": {
"Python": {
"code_actions_on_format": {
// Organize imports
"source.organizeImports.ruff": true,
// Fix all auto-fixable lint violations
"source.fixAll.ruff": true
}
}
}
}File: site/ruff/faq/index.md
[FAQ](#faq)
[Is the Ruff linter compatible with Black?](#is-the-ruff-linter-compatible-with-black)
Yes. The Ruff linter is compatible with Black out-of-the-box, as long as the line-length setting is consistent between the two.
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's linter and Black treat line-length enforcement a little differently. Black, like Ruff's formatter, makes a best-effort attempt to adhere to the line-length, but avoids automatic line-wrapping in some cases (e.g., within comments). Ruff, on the other hand, will flag line-too-long (E501) for any line that exceeds the line-length setting. As such, if line-too-long (E501) is enabled, Ruff can still trigger line-length violations even when Black or ruff format is enabled.
[How does Ruff's formatter compare to Black?](#how-does-ruffs-formatter-compare-to-black)
The Ruff formatter is designed to be a drop-in replacement for 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 Style Guide for more.
[How does Ruff's linter compare to Flake8?](#how-does-ruffs-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.
Under those conditions, Ruff implements every rule in Flake8. In practice, that means Ruff implements all of the F rules (which originate from Pyflakes), along with a subset of the E and W rules (which originate from pycodestyle).
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools natively, including:
- autoflake
- eradicate
- flake8-2020
- flake8-annotations
- flake8-async
- flake8-bandit (#1646)
- flake8-blind-except
- flake8-boolean-trap
- flake8-bugbear
- flake8-builtins
- flake8-commas
- flake8-comprehensions
- flake8-copyright
- flake8-datetimez
- flake8-debugger
- flake8-django
- flake8-docstrings
- flake8-eradicate
- flake8-errmsg
- flake8-executable
- flake8-gettext
- flake8-implicit-str-concat
- flake8-import-conventions
- flake8-logging
- flake8-logging-format
- flake8-no-pep420
- flake8-pie
- flake8-print
- flake8-pyi
- flake8-pytest-style
- flake8-quotes
- flake8-raise
- flake8-return
- flake8-self
- flake8-simplify
- flake8-slots
- flake8-super
- flake8-tidy-imports
- flake8-todos
- flake8-type-checking
- flake8-use-pathlib
- flynt (#2102)
- isort
- mccabe
- pandas-vet
- pep8-naming
- perflint (#4789)
- pydocstyle
- pygrep-hooks
- pyupgrade
- tryceratops
- yesqa
Note that, in some cases, Ruff uses different rule codes and prefixes than would be found in the originating Flake8 plugins. For example, Ruff uses TID252 to represent the I252 rule from flake8-tidy-imports. This helps minimize conflicts across plugins and allows any individual plugin to be toggled on or off with a single (e.g.) --select TID, as opposed to --select I2 (to avoid conflicts with the isort rules, like I001).
Beyond the rule set, Ruff's primary limitation vis-à-vis Flake8 is that it does not support custom lint rules. (Instead, popular Flake8 plugins are re-implemented in Rust as part of Ruff itself.) One minor difference is that Ruff doesn't include all the 'opinionated' rules from flake8-bugbear.
[How does Ruff's linter compare to Pylint?](#how-does-ruffs-linter-compare-to-pylint)
At time of writing, Pylint implements ~409 total rules, while Ruff implements over 900, of which at least 209 overlap with the Pylint rule set (see: #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, Ruff is not a "pure" drop-in replacement for Pylint (and vice versa), as they enforce different sets of rules.
Despite these differences, many users have successfully switched from Pylint to Ruff, especially those using Ruff alongside a type checker, which can cover some of the functionality that Pylint provides.
Like Flake8, Pylint supports plugins (called "checkers"), while Ruff implements all rules natively and does not support custom or third-party rules. Unlike Pylint, Ruff is capable of automatically fixing its own lint violations.
In some cases, Ruff's rules may yield slightly different results than their Pylint counterparts. For example, Ruff's too-many-branches does not count try blocks as their own branches, unlike Pylint's R0912. Ruff's PL rule group also includes a small number of rules from Pylint extensions (like magic-value-comparison), which need to be explicitly activated when using Pylint. By enabling Ruff's PL group, you may see violations for rules that weren't previously enabled through your Pylint configuration.
Pylint parity is being tracked in #970.
[How does Ruff compare to Mypy, or Pyright, or Pyre?](#how-does-ruff-compare-to-mypy-or-pyright-or-pyre)
Ruff is a linter, not a type checker. It can detect some of the same problems that a type checker can, but a type checker will catch certain errors that Ruff would miss. The opposite is also true: Ruff will catch certain errors that a type checker would typically ignore.
For example, unlike a type checker, Ruff will notify you if an import is unused, by looking for references to that import in the source code; on the other hand, a type checker could flag that you passed an integer argument to a function that expects a string, which Ruff would miss. The tools are complementary.
It's recommended that you use Ruff in conjunction with a type checker, like Mypy, Pyright, or Pyre, with Ruff providing faster feedback on lint violations and the type checker providing more detailed feedback on type errors.
[Which tools does Ruff replace?](#which-tools-does-ruff-replace)
Today, Ruff can be used to replace Flake8 when used with any of the following plugins:
- flake8-2020
- flake8-annotations
- flake8-async
- flake8-bandit (#1646)
- flake8-blind-except
- flake8-boolean-trap
- flake8-bugbear
- flake8-builtins
- flake8-commas
- flake8-comprehensions
- flake8-copyright
- flake8-datetimez
- flake8-debugger
- flake8-django
- flake8-docstrings
- flake8-eradicate
- flake8-errmsg
- flake8-executable
- flake8-gettext
- flake8-implicit-str-concat
- flake8-import-conventions
- flake8-logging
- flake8-logging-format
- flake8-no-pep420
- flake8-pie
- flake8-print
- flake8-pytest-style
- flake8-quotes
- flake8-raise
- flake8-return
- flake8-self
- flake8-simplify
- flake8-slots
- flake8-super
- flake8-tidy-imports
- flake8-todos
- flake8-type-checking
- flake8-use-pathlib
- flynt (#2102)
- mccabe
- pandas-vet
- pep8-naming
- perflint (#4789)
- pydocstyle
- tryceratops
Ruff can also replace Black, isort, yesqa, eradicate, and most of the rules implemented in pyupgrade.
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an issue.
[Do I have to use Ruff's linter and formatter together?](#do-i-have-to-use-ruffs-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?](#what-versions-of-python-does-ruff-support)
Ruff can lint code for any Python version from 3.7 onwards, including Python 3.13.
Ruff does not support Python 2. Ruff may run on pre-Python 3.7 code, although such versions are not officially supported (e.g., Ruff does not respect type comments).
Ruff is installable under any Python version from 3.7 onwards.
[Do I need to install Rust to use Ruff?](#do-i-need-to-install-rust-to-use-ruff)
Nope! Ruff is available as ruff on PyPI. We recommend installing Ruff with uv, though it's also installable with pip, pipx, and a variety of other package managers:
$ # Install Ruff globally.
$ uv tool install ruff@latest
$ # Or add Ruff to your project.
$ uv add --dev ruff
$ # With pip.
$ pip install ruff
$ # With pipx.
$ pipx install ruffStarting with version 0.5.0, Ruff can also be installed with our standalone installers:
$ # On macOS and Linux.
$ curl -LsSf https://astral.sh/ruff/install.sh | sh
$ # On Windows.
$ powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
$ # For a specific version.
$ curl -LsSf https://astral.sh/ruff/0.5.0/install.sh | sh
$ powershell -c "irm https://astral.sh/ruff/0.5.0/install.ps1 | iex"Ruff ships with wheels for all major platforms, which enables uv, pip, and other tools to install Ruff without relying on a Rust toolchain at all.
[Can I write my own linter 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 for more.
\[How does Ruff's import sorting compare to [isort](https://pypi.org/project/isort/)?\](#how-does-ruffs-import-sorting-compare-to-isort)
Ruff's import sorting is intended to be near-equivalent to isort's when using isort's profile = "black".
There are a few known differences in how Ruff and isort treat aliased imports, and in how Ruff and isort treat inline comments in some cases (see: #1381, #2104).
For example, Ruff tends to group non-aliased imports from the same module:
from numpy import cos, int8, int16, int32, int64, tan, uint8, uint16, uint32, uint64
from numpy import sin as np_sinWhereas isort splits them into separate import statements at each aliased boundary:
from numpy import cos, int8, int16, int32, int64
from numpy import sin as np_sin
from numpy import tan, uint8, uint16, uint32, uint64Ruff also correctly classifies some modules as standard-library that aren't recognized by isort, like _string and idlelib.
Like isort, Ruff's import sorting is compatible with Black.
[How does Ruff determine which of my imports are first-party, third-party, etc.?](#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc)
Ruff accepts a src option that in your pyproject.toml, ruff.toml, or .ruff.toml file, specifies the directories that Ruff should consider when determining whether an import is first-party.
For example, if you have a project with the following structure:
my_project
├── pyproject.toml
└── src
└── foo
├── __init__.py
└── bar
├── __init__.py
└── baz.pyWhen Ruff sees an import like import foo, it will then iterate over the src directories, looking for a corresponding Python module (in reality, a directory named foo or a file named foo.py). For module paths with multiple components like import foo.bar, Ruff will require that the full relative path foo/bar exists as a directory, or that foo/bar.py or foo/bar.pyi exist as files. Finally, for imports of the form from foo import bar, Ruff will only use foo when determining whether a module is first-party or third-party.
If there is a directory whose name matches a third-party package, but does not contain Python code, it could happen that the above algorithm incorrectly infers an import to be first-party. To prevent this, you can modify the known-third-party setting. For example, if you import the package wandb but also have a subdirectory of your src with the same name, you can add the following:
[tool.ruff.lint.isort]
known-third-party = ["wandb"][lint.isort]
known-third-party = ["wandb"]If the src field is omitted, Ruff will default to using the "project root", along with a "src" subdirectory, as the first-party sources, to support both flat and nested project layouts. The "project root" is typically the directory containing your pyproject.toml, ruff.toml, or .ruff.toml file, unless a configuration file is provided on the command-line via the --config option, in which case, the current working directory is used as the project root.
In this case, Ruff would check the "src" directory by default, but we can configure it as an explicit, exclusive first-party source like so:
[tool.ruff]
# Ruff supports a top-level `src` option in lieu of isort's `src_paths` setting.
# All paths are relative to the project root, which is the directory containing the pyproject.toml.
src = ["src"]# Ruff supports a top-level `src` option in lieu of isort's `src_paths` setting.
# All paths are relative to the project root, which is the directory containing the pyproject.toml.
src = ["src"]If your pyproject.toml, ruff.toml, or .ruff.toml extends another configuration file, Ruff will still use the directory containing your pyproject.toml, ruff.toml, or .ruff.toml file as the project root (as opposed to the directory of the file pointed to via the extends option).
For example, if you add a configuration file to the tests directory in the above example, you'll want to explicitly set the src option in the extended configuration file:
[tool.ruff]
extend = "../pyproject.toml"
src = ["../src"]extend = "../pyproject.toml"
src = ["../src"]Beyond this src-based detection, Ruff will also attempt to determine the current Python package for a given Python file (determined via the existence of a __init__.py file in a directory), and mark imports from within the same package as first-party. For example, above, baz.py would be identified as part of the Python package beginning at ./my_project/src/foo, and so any imports in baz.py that begin with foo (like import foo.bar) would be considered first-party based on this same-package heuristic.
For a detailed explanation of src resolution, see the contributing guide.
Ruff can also be configured to treat certain modules as (e.g.) always first-party, regardless of their location on the filesystem. For example, you can set known-first-party like so:
[tool.ruff]
src = ["src", "tests"]
[tool.ruff.lint]
select = [
# Pyflakes
"F",
# Pycodestyle
"E",
"W",
# isort
"I001"
]
[tool.ruff.lint.isort]
known-first-party = ["my_module1", "my_module2"]src = ["src", "tests"]
[lint]
select = [
# Pyflakes
"F",
# Pycodestyle
"E",
"W",
# isort
"I001"
]
[lint.isort]
known-first-party = ["my_module1", "my_module2"]Ruff does not yet support all of isort's configuration options, though it does support many of them. You can find the supported settings in the API reference.
[Does Ruff support Jupyter Notebooks?](#does-ruff-support-jupyter-notebooks)
Ruff has built-in support for linting and formatting Jupyter Notebooks. Refer to the Jupyter Notebook section for more details.
Ruff also integrates with nbQA, a tool for running linters and code formatters over Jupyter Notebooks.
After installing ruff and nbqa, you can run Ruff over a notebook like so:
$ nbqa ruff Untitled.ipynb
Untitled.ipynb:cell_1:2:5: F841 Local variable `x` is assigned to but never used
Untitled.ipynb:cell_2:1:1: E402 Module level import not at top of file
Untitled.ipynb:cell_2:1:8: F401 `os` imported but unused
Found 3 errors.
1 potentially fixable with the `--fix` option.[Does Ruff support NumPy- or Google-style docstrings?](#does-ruff-support-numpy-or-google-style-docstrings)
Yes! To enforce a docstring convention, add a convention setting following to your configuration file:
[tool.ruff.lint.pydocstyle]
convention = "google" # Accepts: "google", "numpy", or "pep257".[lint.pydocstyle]
convention = "google" # Accepts: "google", "numpy", or "pep257".For example, if you're coming from flake8-docstrings, and your originating configuration uses --docstring-convention=numpy, you'd instead set convention = "numpy" in your pyproject.toml, as above.
Alongside convention, you'll want to explicitly enable the D rule code prefix, since the D rules are not enabled by default:
[tool.ruff.lint]
select = ["D"]
[tool.ruff.lint.pydocstyle]
convention = "google"[lint]
select = ["D"]
[lint.pydocstyle]
convention = "google"Enabling a convention will disable any rules that are not included in the specified convention. As such, the intended workflow is to enable a convention and then selectively enable or disable any additional rules on top of it:
[tool.ruff.lint]
select = [
"D",
# Augment the convention by requiring an imperative mood for all docstrings.
"D401",
]
ignore = [
# Relax the convention by _not_ requiring documentation for every function parameter.
"D417",
]
[tool.ruff.lint.pydocstyle]
convention = "google"[lint]
select = [
"D",
# Augment the convention by requiring an imperative mood for all docstrings.
"D401",
]
ignore = [
# Relax the convention by _not_ requiring documentation for every function parameter.
"D417",
]
[lint.pydocstyle]
convention = "google"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 for more details; or, to see which rules are currently in preview, visit the rules reference.
[How can I tell what settings Ruff is using to check my code?](#how-can-i-tell-what-settings-ruff-is-using-to-check-my-code)
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`. What are my options?](#i-want-to-use-ruff-but-i-dont-want-to-use-pyprojecttoml-what-are-my-options)
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 file can omit the [tool.ruff] section header. For example:
[tool.ruff]
line-length = 88
[tool.ruff.lint.pydocstyle]
convention = "google"line-length = 88
[lint.pydocstyle]
convention = "google"Ruff doesn't currently support INI files, like setup.cfg or tox.ini.
[How can I change Ruff's default configuration?](#how-can-i-change-ruffs-default-configuration)
When no configuration file is found, Ruff will look for a user-specific ruff.toml file as a last resort. This behavior is similar to Flake8's ~/.config/flake8.
On macOS and Linux, Ruff expects that file to be located at ~/.config/ruff/ruff.toml, and respects the XDG_CONFIG_HOME specification.
On Windows, Ruff expects that file to be located at ~\AppData\Roaming\ruff\ruff.toml.
Note
Prior to v0.5.0, Ruff would read user-specific configuration from ~/Library/Application Support/ruff/ruff.toml on macOS. While Ruff will still respect such configuration files, the use of ~/Library/Application Support is considered deprecated.
For more, see the etcetera crate.
[Ruff tried to fix something — but it broke my code. What's going on?](#ruff-tried-to-fix-something-but-it-broke-my-code-whats-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 setting, or passing the --unsafe-fixes flag to ruff check. For more, see the fix documentation.
Even still, given the dynamic nature of Python, it's difficult to have complete certainty when making changes to code, even for seemingly trivial fixes. If a "safe" fix breaks your code, please file an Issue.
[How can I disable/force Ruff's color output?](#how-can-i-disableforce-ruffs-color-output)
Ruff's color output is powered by the colored crate, which attempts to automatically detect whether the output stream supports color. However, you can force colors off by setting the NO_COLOR environment variable to any value (e.g., NO_COLOR=1), or force colors on by setting FORCE_COLOR to any non-empty value (e.g., FORCE_COLOR=1).
colored also supports the CLICOLOR and CLICOLOR_FORCE environment variables (see the spec).
[Ruff behaves unexpectedly when using `source.*` code actions in Notebooks. What's going on?](#source-code-actions-in-notebooks)
Ruff does not support source.organizeImports and source.fixAll code actions in Jupyter Notebooks (notebook.codeActionsOnSave in VS Code). It's recommended to use the notebook prefixed code actions for the same such as notebook.source.organizeImports and notebook.source.fixAll respectively.
Ruff requires to have a full view of the notebook to provide accurate diagnostics and fixes. For example, if you have a cell that imports a module and another cell that uses that module, Ruff needs to see both cells to mark the import as used. If Ruff were to only see one cell at a time, it would incorrectly mark the import as unused.
When using the source.* code actions for a Notebook, Ruff will be asked to fix any issues for each cell in parallel, which can lead to unexpected behavior. For example, if a user has configured to run source.organizeImports code action on save for a Notebook, Ruff will attempt to fix the imports for the entire notebook corresponding to each cell. This leads to the client making the same changes to the notebook multiple times, which can lead to unexpected behavior (astral-sh/ruff-vscode#680, astral-sh/ruff-vscode#640, astral-sh/ruff-vscode#391).
File: site/ruff/formatter/index.md
[The Ruff Formatter](#the-ruff-formatter)
The Ruff formatter is an extremely fast Python code formatter designed as a drop-in replacement for Black, available as part of the ruff CLI via ruff format.
[`ruff format`](#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:
ruff format # Format all files in the current directory.
ruff format path/to/code/ # Format all files in `path/to/code` (and any subdirectories).
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](#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, 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: Style Guide.)
Given this focus on Black compatibility, the formatter thus adheres to Black's (stable) code style, which aims for "consistency, generality, readability and reducing git diffs". To give you a sense for the enforced code style, here's an example:
# 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.)
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). 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 mode.
[Configuration](#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, indentation style and formatting code examples in docstrings).
For example, to configure the formatter to use single quotes, format code examples in docstrings, a line width of 100, and tab indentation, add the following to your configuration file:
[tool.ruff]
line-length = 100
[tool.ruff.format]
quote-style = "single"
indent-style = "tab"
docstring-code-format = trueline-length = 100
[format]
quote-style = "single"
indent-style = "tab"
docstring-code-format = trueFor the full list of supported settings, see Settings. For more on configuring Ruff via pyproject.toml, see Configuring Ruff.
Given the focus on Black compatibility (and unlike formatters like YAPF), Ruff does not currently expose any other configuration options.
[Docstring formatting](#docstring-formatting)
The Ruff formatter provides an opt-in feature for automatically formatting Python code examples in docstrings. The Ruff formatter currently recognizes code examples in the following formats:
- The Python doctest format.
- CommonMark fenced code blocks with the following info strings:
python,py,python3, orpy3. Fenced code blocks without an info string are assumed to be Python code examples and also formatted. - reStructuredText literal blocks. While literal blocks may contain things other than Python, this is meant to reflect a long-standing convention in the Python ecosystem where literal blocks often contain Python code.
- reStructuredText
code-blockandsourcecodedirectives. As with Markdown, the language names recognized for Python arepython,py,python3, orpy3.
If a code example is recognized and treated as Python, the Ruff formatter will automatically skip it if the code does not parse as valid Python or if the reformatted code would produce an invalid Python program.
Users may also configure the line length limit used for reformatting Python code examples in docstrings. The default is a special value, dynamic, which instructs the formatter to respect the line length limit setting for the surrounding Python code. The dynamic setting ensures that even when code examples are found inside indented docstrings, the line length limit configured for the surrounding Python code will not be exceeded. Users may also configure a fixed line length limit for code examples in docstrings.
For example, this configuration shows how to enable docstring code formatting with a fixed line length limit:
[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = 20[format]
docstring-code-format = true
docstring-code-line-length = 20With the above configuration, this code:
def f(x):
'''
Something about `f`. And an example:
.. code-block:: python
foo, bar, quux = this_is_a_long_line(lion, hippo, lemur, bear)
'''
pass... will be reformatted (assuming the rest of the options are set to their defaults) as:
def f(x):
"""
Something about `f`. And an example:
.. code-block:: python
(
foo,
bar,
quux,
) = this_is_a_long_line(
lion,
hippo,
lemur,
bear,
)
"""
pass[Markdown code formatting](#markdown-code-formatting)
This feature is currently only available in preview mode.
The Ruff formatter can also format Python code blocks in Markdown files. In these files, Ruff will format any CommonMark fenced code blocks with the following info strings: python, py, python3, py3, or pyi. The formatter will automatically skip a code block if the code does not parse as valid Python or if the reformatted code would produce an invalid Python program.
Code blocks marked as python, py, python3, or py3 will be formatted with the normal Python code formatting style, while any code blocks marked with pyi will be formatted like Python type stub files:
```py
print("hello")def foo(): ...
def bar(): ...Ruff also supports [Quarto](https://quarto.org/) style executable code blocks
with curly braces surrounding the language name:print("hello")While [formatting suppression](#format-suppression) comments will be handled as
usual within code blocks, the formatter will also skip formatting any code block
surrounded by appropriate HTML comments, such as:print( 'hello' )Any number of code blocks may be contained within a matching pair of `off` and
`on` HTML comments, and any `off` comment *without* a matching `on` comment
will implicitly cover the remaining portion of the document.
The Ruff formatter will also recognize HTML comments from [blacken-docs](https://github.com/adamchainz/blacken-docs/),
`` and ``, which are equivalent
to `` and `` respectively.
To format Markdown files with extensions other than `.md`, configure custom
[`extension`](../settings/#extension) mappings. Ruff will automatically include
these mapped extensions in file discovery:[tool.ruff]
Treat `.mdx` and `.qmd` files as Markdown
extension = { mdx = "markdown", qmd = "markdown" }
Treat `.mdx` and `.qmd` files as Markdown
extension = {mdx="markdown", qmd="markdown"}
If you run Ruff via [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit), Markdown
support needs to be explicitly included by adding it to `types_or`:
.pre-commit-config.yamlrepos:
- repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.16 hooks:
- id: ruff-format types_or: [python, pyi, jupyter, markdown]
To *disable* formatting of Markdown files, add them to
[`extend-exclude`](../settings/#extend-exclude) in your project settings:[tool.ruff]
Disable formatting in Markdown files
extend-exclude = ["*.md"]
Disable formatting in Markdown files
extend-exclude = ["*.md"]
## [Format suppression](#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: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`:[ # fmt: off '1', # fmt: on '2', ]
Instead, apply the `# fmt: off` comment to the entire statement:fmt: off
[ '1', '2', ]
fmt: on
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.
`# fmt: skip` comments suppress formatting for a case header, decorator,
function definition, class definition, or the preceding statements
on the same logical line. The formatter leaves the following unchanged:if True: pass elif False: # fmt: skip pass
@Test @Test2(a,b) # fmt: skip def test(): ...
a = [1,2,3,4,5] # fmt: skip
def test(a,b,c,d,e,f) -> int: # fmt: skip pass
x=1;x=2;x=3 # fmt: skip
Adding a `# fmt: skip` comment at the end of an expression will have no effect. In
the following example, the list entry `'1'` will be formatted, despite the `# fmt: skip`:a = call( [ '1', # fmt: skip '2', ], b )
Instead, apply the `# fmt: skip` comment to the entire statement:a = call( [ '1', '2', ], b ) # fmt: skip
## [Conflicting lint rules](#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 configured appropriately, the goal of Ruff's formatter-linter compatibility is
such that running the formatter should never introduce new lint errors.
When using Ruff as a formatter, we recommend avoiding the following lint rules:
- [`tab-indentation`](../rules/tab-indentation/) (`W191`)
- [`indentation-with-invalid-multiple`](../rules/indentation-with-invalid-multiple/) (`E111`)
- [`indentation-with-invalid-multiple-comment`](../rules/indentation-with-invalid-multiple-comment/) (`E114`)
- [`over-indented`](../rules/over-indented/) (`E117`)
- [`incorrect-blank-line-before-class`](../rules/incorrect-blank-line-before-class/) (`D203`)
- [`docstring-tab-indentation`](../rules/docstring-tab-indentation/) (`D206`)
- [`triple-single-quotes`](../rules/triple-single-quotes/) (`D300`)
- [`bad-quotes-inline-string`](../rules/bad-quotes-inline-string/) (`Q000`)
- [`bad-quotes-multiline-string`](../rules/bad-quotes-multiline-string/) (`Q001`)
- [`bad-quotes-docstring`](../rules/bad-quotes-docstring/) (`Q002`)
- [`avoidable-escaped-quote`](../rules/avoidable-escaped-quote/) (`Q003`)
- [`unnecessary-escaped-quote`](../rules/unnecessary-escaped-quote/) (`Q004`)
- [`missing-trailing-comma`](../rules/missing-trailing-comma/) (`COM812`)
- [`prohibited-trailing-comma`](../rules/prohibited-trailing-comma/) (`COM819`)
- [`multi-line-implicit-string-concatenation`](../rules/multi-line-implicit-string-concatenation/) (`ISC002`) if used without `ISC001` and `flake8-implicit-str-concat.allow-multiline = false`
While the [`line-too-long`](../rules/line-too-long/) (`E501`) rule *can* be used alongside the
formatter, the formatter only makes a best-effort attempt to wrap lines at the configured
[`line-length`](../settings/#line-length). As such, formatted code *may* exceed the line length,
leading to [`line-too-long`](../rules/line-too-long/) (`E501`) errors.
None of the above are included in Ruff's default configuration. However, if you've enabled
any of these rules or their parent categories (like `Q`), we recommend disabling them via the
linter's [`lint.ignore`](../settings/#lint_ignore) setting.
Similarly, we recommend avoiding 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/#lint_isort_force-single-line)
- [`force-wrap-aliases`](../settings/#lint_isort_force-wrap-aliases)
- [`lines-after-imports`](../settings/#lint_isort_lines-after-imports)
- [`lines-between-types`](../settings/#lint_isort_lines-between-types)
- [`split-on-trailing-comma`](../settings/#lint_isort_split-on-trailing-comma)
If you've configured any of these settings to take on non-default values, we recommend removing
them from your Ruff configuration.
When an incompatible lint rule or setting is enabled, `ruff format` will emit a warning. If your
`ruff format` is free of warnings, you're good to go!
## [Exit codes](#exit-codes)
`ruff format` exits with the following status codes:
- `0` if Ruff terminates successfully, regardless of whether any files were formatted.
- `1` if Ruff terminates successfully, one or more files were formatted, and `--exit-non-zero-on-format` was specified.
- `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.
## [Style Guide](#style-guide)
The formatter is designed to be a drop-in replacement for [Black](https://github.com/psf/black).
This section documents the areas where the Ruff formatter goes beyond Black in terms of code style.
### [Intentional deviations](#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*](black/).
Unintentional deviations from Black are tracked in the [issue tracker](https://github.com/astral-sh/ruff/issues?q=is%3Aopen+is%3Aissue+label%3Aformatter).
If you've identified a new deviation, please [file an issue](https://github.com/astral-sh/ruff/issues/new).
### [Preview style](#preview-style)
Similar to [Black](https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html#preview-style), Ruff implements formatting changes
under the [`preview`](https://docs.astral.sh/ruff/settings/#format_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).
### [F-string formatting](#f-string-formatting)
*Stabilized in Ruff 0.9.0*
Unlike Black, Ruff formats the expression parts of f-strings which are the parts inside the curly
braces `{...}`. This is a [known deviation](black/#f-strings) from Black.
Ruff employs several heuristics to determine how an f-string should be formatted which are detailed
below.
#### [Quotes](#quotes)
Ruff will use the [configured quote style](../settings/#format_quote-style) for the f-string expression unless doing so would result in
invalid syntax for the target Python version or requires more backslash escapes than the original
expression. Specifically, Ruff will preserve the original quote style for the following cases:
When the target Python version is < 3.12 and a [self-documenting f-string](https://realpython.com/python-f-strings/#self-documenting-expressions-for-debugging) contains a string
literal with the [configured quote style](../settings/#format_quote-style):format.quote-style = "double"
f'{10 + len("hello")=}'
This f-string cannot be formatted as follows when targeting Python < 3.12
f"{10 + len("hello")=}"
When the target Python version is < 3.12 and an f-string contains any triple-quoted string, byte
or f-string literal that contains the [configured quote style](../settings/#format_quote-style):format.quote-style = "double"
f'{"""nested " """}'
This f-string cannot be formatted as follows when targeting Python < 3.12
f"{'''nested " '''}"
For all target Python versions, when a [self-documenting f-string](https://realpython.com/python-f-strings/#self-documenting-expressions-for-debugging) contains an expression between
the curly braces (`{...}`) with a format specifier containing the [configured quote style](../settings/#format_quote-style):format.quote-style = "double"
f'{1=:"foo}'
This f-string cannot be formatted as follows for all target Python versions
f"{1=:"foo}"
By default, or when targeting Python versions below 3.12, Ruff alternates quote styles for nested
f-strings, starting with the [configured quote style](../settings/#format_quote-style) for the outermost f-string.
For example, consider the following f-string:format.quote-style = "double"
f"outer f-string {f"nested f-string {f"another nested f-string"} end"} end"
With default settings, Ruff formats it as:f"outer f-string {f'nested f-string {f"another nested f-string"} end'} end"
When targeting Python 3.12+ and with `nested-string-quote-style = "preferred"`,
Ruff will use the configured quote style for nested strings:f"outer f-string {f"nested f-string {f"another nested f-string"} end"} end"
#### [Line breaks](#line-breaks)
Starting with Python 3.12 ([PEP 701](https://peps.python.org/pep-0701/)), the expression parts of an f-string can
span multiple lines. Ruff needs to decide when to introduce a line break in an f-string expression.
This depends on the semantic content of the expression parts of an f-string - for example,
introducing a line break in the middle of a natural-language sentence is undesirable. Since Ruff
doesn't have enough information to make that decision, it adopts a heuristic similar to [Prettier](https://prettier.io/docs/en/next/rationale.html#template-literals):
it will only split the expression parts of an f-string across multiple lines if there was already a line break
within any of the expression parts.
For example, the following code:f"this f-string has a multiline expression { ['red', 'green', 'blue', 'yellow',]} and does not fit within the line length"
... is formatted as:The list expression is split across multiple lines because of the trailing comma
f"this f-string has a multiline expression { [ 'red', 'green', 'blue', 'yellow', ] } and does not fit within the line length"
But, the following will not be split across multiple lines even though it exceeds the line length:f"this f-string has a multiline expression {['red', 'green', 'blue', 'yellow']} and does not fit within the line length"
If you want Ruff to split an f-string across multiple lines, ensure there's a linebreak somewhere within the
`{...}` parts of an f-string.
### [Fluent layout for method chains](#fluent-layout-for-method-chains)
At times, when developers write long chains of methods on an object, such asx = df.filter(cond).agg(func).merge(other)
the intent is to perform a sequence of transformations or operations
on a fixed object of interest - in this example, the object `df`.
Assuming the assigned expression exceeds the `line-length`, this preview
style will format the above as:x = ( df .filter(cond) .agg(func) .merge(other) )
This deviates from the stable formatting, and also from Black, both
of which would produce:x = ( df.filter(cond) .agg(func) .merge(other) )
Both the stable and preview formatting are variants of something
called a **fluent layout**.
In general, this preview style differs from the stable style
only at the first attribute that precedes
a call or subscript. The preview formatting breaks *before* this attribute,
while the stable formatting breaks *after* the call or subscript.
## [Sorting imports](#sorting-imports)
Currently, the Ruff formatter does not sort imports. In order to both sort imports and format,
call the Ruff linter and then the formatter:ruff check --select I --fix ruff format
A unified command for both linting and formatting is [planned](https://github.com/astral-sh/ruff/issues/8232).```File: site/ruff/formatter/black/index.md
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.
[Trailing end-of-line comments](#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:
# 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, 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-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 for details.
This is similar to Pyink but a deviation from Black. Black avoids splitting any lines that contain a # type comment (#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():
# Input
[
first(), # noqa
second()
]
# Black
[first(), second()] # noqa
# Ruff
[
first(), # noqa
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 uses Unicode width for strings, and character width for all other tokens. Ruff also uses Unicode width for identifiers and comments.
[Parenthesizing long nested-expressions](#parenthesizing-long-nested-expressions)
Black 24 and newer parenthesizes long conditional expressions and type annotations in function parameters:
# Black
[
"____________________________",
"foo",
"bar",
(
"baz"
if some_really_looooooooong_variable
else "some other looooooooooooooong value"
),
]
def foo(
i: int,
x: (
Loooooooooooooooooooooooong
| Looooooooooooooooong
| Looooooooooooooooooooong
| Looooooong
),
*,
s: str,
) -> None:
pass
# Ruff
[
"____________________________",
"foo",
"bar",
"baz"
if some_really_looooooooong_variable
else "some other looooooooooooooong value",
]
def foo(
i: int,
x: Loooooooooooooooooooooooong
| Looooooooooooooooong
| Looooooooooooooooooooong
| Looooooong,
*,
s: str,
) -> None:
passWe agree that Ruff's formatting (that matches Black's 23) is hard to read and needs improvement. But we aren't convinced that parenthesizing long nested expressions is the best solution, especially when considering expression formatting holistically. That's why we want to defer the decision until we've explored alternative nested expression formatting styles. See psf/Black#4123 for an in-depth explanation of our concerns and an outline of possible alternatives.
[Call expressions with a single multiline string argument](#call-expressions-with-a-single-multiline-string-argument)
Unlike Black, Ruff preserves the indentation of a single multiline-string argument in a call expression:
# Input
call(
""""
A multiline
string
"""
)
dedent(""""
A multiline
string
""")
# Black
call(
""""
A multiline
string
"""
)
dedent(
""""
A multiline
string
"""
)
# Ruff
call(
""""
A multiline
string
"""
)
dedent(""""
A multiline
string
""")Black intended to ship a similar style change as part of the 2024 style that always removes the indent. It turned out that this change was too disruptive to justify the cases where it improved formatting. Ruff introduced the new heuristic of preserving the indent. We believe it's a good compromise that improves formatting but minimizes disruption for users.
[Blank lines at the start of a block](#blank-lines-at-the-start-of-a-block)
Black 24 and newer allows blank lines at the start of a block, where Ruff always removes them:
# Black
if x:
a = 123
# Ruff
if x:
a = 123Currently, we are concerned that allowing blank lines at the start of a block leads to unintentional blank lines when refactoring or moving code. However, we will consider adopting Black's formatting at a later point with an improved heuristic. The style change is tracked in #9745.
[F-strings](#f-strings)
Ruff formats expression parts in f-strings whereas Black does not:
# Input
f'test{inner + "nested_string"} including math {5 ** 3 + 10}'
# Black
f'test{inner + "nested_string"} including math {5 ** 3 + 10}'
# Ruff
f"test{inner + 'nested_string'} including math {5**3 + 10}"For more details on the formatting style, refer to the f-string formatting section.
[Implicit concatenated strings](#implicit-concatenated-strings)
Ruff merges implicitly concatenated strings if the entire string fits on a single line:
# Input
def test(max_history):
raise argparse.ArgumentTypeError(
f"The value of `--max-history {max_history}` " f"is not a positive integer."
)
# Black
def test(max_history):
raise argparse.ArgumentTypeError(
f"The value of `--max-history {max_history}` " f"is not a positive integer."
)
# Ruff
def test(max_history):
raise argparse.ArgumentTypeError(
f"The value of `--max-history {max_history}` is not a positive integer."
)Black's unstable style applies the same formatting.
There are few rare cases where Ruff can't merge the implicitly concatenated strings automatically. In those cases, Ruff preserves if the implicit concatenated strings are formatted over multiple lines:
# Input
a = (
r"aaaaaaa"
"bbbbbbbbbbbb"
)
# Black
a = r"aaaaaaa" "bbbbbbbbbbbb"
# Ruff
a = (
r"aaaaaaa"
"bbbbbbbbbbbb"
)This ensures compatibility with ISC001 (#8272).
[`assert` statements](#assert-statements)
Unlike Black, Ruff prefers breaking the message over breaking the assertion, similar to how both Ruff and Black prefer breaking the assignment value over breaking the assignment target:
# Input
assert (
len(policy_types) >= priority + num_duplicates
), f"This tests needs at least {priority+num_duplicates} many types."
# Black
assert (
len(policy_types) >= priority + num_duplicates
), f"This tests needs at least {priority+num_duplicates} many types."
# Ruff
assert len(policy_types) >= priority + num_duplicates, (
f"This tests needs at least {priority + num_duplicates} many types."
)[`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:
# 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[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:
# 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](#parentheses-around-awaited-collections-are-not-preserved)
Black preserves parentheses around awaited collections:
await ([1, 2, 3])Ruff will instead remove them:
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](#implicit-string-concatenations-in-attribute-accesses)
Given the following unformatted code:
print("aaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaa".format(bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb))Internally, Black's logic will first expand the outermost print call:
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:
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:
print(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".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](#own-line-comments-on-expressions-dont-cause-the-expression-to-expand)
Given an expression like:
(
# 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:
(
# 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:
(
# A comment in the middle
some_example_var and some_example_var not in some_example_var
)[Tuples are parenthesized when expanded](#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:
# 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:
# Input
for a, [b, d,] in c:
pass
# Black
for a, [
b,
d,
] in c:
pass
# Ruff
for a, [
b,
d,
] in c:
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:
# 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).
[Parentheses around call-chain assignment values are not preserved](#parentheses-around-call-chain-assignment-values-are-not-preserved)
Given:
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:
# 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[Call chain calls break differently in some cases](#call-chain-calls-break-differently-in-some-cases)
Black occasionally breaks call chains differently than Ruff; in particular, Black occasionally expands the arguments for the last call in the chain, as in:
# 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:
# 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)Similarly, in some cases, Ruff will collapse composite binary expressions more aggressively than Black, if doing so allows the expression to fit within the configured line width:
# Black
assert AAAAAAAAAAAAAAAAAAAAAA.bbbbbb.fooo(
aaaaaaaaaaaa=aaaaaaaaaaaa
).ccccc() == (len(aaaaaaaaaa) + 1) * fooooooooooo * (
foooooo + 1
) * foooooo * len(
list(foo(bar(4, foo), foo))
)
# Ruff
assert AAAAAAAAAAAAAAAAAAAAAA.bbbbbb.fooo(
aaaaaaaaaaaa=aaaaaaaaaaaa
).ccccc() == (len(aaaaaaaaaa) + 1) * fooooooooooo * (
foooooo + 1
) * foooooo * len(list(foo(bar(4, foo), foo)))[Single `with` item targeting Python 3.8 or older](#single-with-item-targeting-python-38-or-older)
Unlike Black, Ruff uses the same layout for with statements with a single context manager as it does for while, if and other compound statements:
# Input
def run(data_path, model_uri):
with pyspark.sql.SparkSession.builder.config(
key="spark.python.worker.reuse", value=True
).config(key="spark.ui.enabled", value=False).master(
"local-cluster[2, 1, 1024]"
).getOrCreate():
# ignore spark log output
spark.sparkContext.setLogLevel("OFF")
print(score_model(spark, data_path, model_uri))
# Black
def run(data_path, model_uri):
with pyspark.sql.SparkSession.builder.config(
key="spark.python.worker.reuse", value=True
).config(key="spark.ui.enabled", value=False).master(
"local-cluster[2, 1, 1024]"
).getOrCreate():
# ignore spark log output
spark.sparkContext.setLogLevel("OFF")
print(score_model(spark, data_path, model_uri))
# Ruff
def run(data_path, model_uri):
with (
pyspark.sql.SparkSession.builder.config(
key="spark.python.worker.reuse", value=True
)
.config(key="spark.ui.enabled", value=False)
.master("local-cluster[2, 1, 1024]")
.getOrCreate()
):
# ignore spark log output
spark.sparkContext.setLogLevel("OFF")
print(score_model(spark, data_path, model_uri))Ruff's formatting matches the formatting of other compound statements:
def test():
if (
pyspark.sql.SparkSession.builder.config(
key="spark.python.worker.reuse", value=True
)
.config(key="spark.ui.enabled", value=False)
.master("local-cluster[2, 1, 1024]")
.getOrCreate()
):
# ignore spark log output
spark.sparkContext.setLogLevel("OFF")
print(score_model(spark, data_path, model_uri))[The last context manager in a `with` statement may be collapsed onto a single line](#the-last-context-manager-in-a-with-statement-may-be-collapsed-onto-a-single-line)
When using a with statement with multiple unparenthesized context managers, Ruff may collapse the last context manager onto a single line, if doing so allows the with statement to fit within the configured line width.
Black, meanwhile, tends to break the last context manager slightly differently, as in the following example:
# Black
with tempfile.TemporaryDirectory() as d1:
symlink_path = Path(d1).joinpath("testsymlink")
with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirectory(
dir=d1
) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTemporaryFile(
dir=d4
) as source_file, tempfile.NamedTemporaryFile(
dir=d3
) as lock_file:
pass
# Ruff
with tempfile.TemporaryDirectory() as d1:
symlink_path = Path(d1).joinpath("testsymlink")
with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirectory(
dir=d1
) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTemporaryFile(
dir=d4
) as source_file, tempfile.NamedTemporaryFile(dir=d3) as lock_file:
passWhen targeting Python 3.9 or newer, parentheses will be inserted around the context managers to allow for clearer breaks across multiple lines, as in:
with tempfile.TemporaryDirectory() as d1:
symlink_path = Path(d1).joinpath("testsymlink")
with (
tempfile.TemporaryDirectory(dir=d1) as d2,
tempfile.TemporaryDirectory(dir=d1) as d4,
tempfile.TemporaryDirectory(dir=d2) as d3,
tempfile.NamedTemporaryFile(dir=d4) as source_file,
tempfile.NamedTemporaryFile(dir=d3) as lock_file,
):
pass[Preserving parentheses around single-element lists](#preserving-parentheses-around-single-element-lists)
Ruff preserves at least one set of parentheses around list elements, even if the list only contains a single element. The Black 2025 style or newer, on the other hand, removes the parentheses for single-element lists if they aren't multiline and doing so does not change semantics:
# Input
items = [(True)]
items = [(((((True)))))]
items = {(123)}
# Black
items = [True]
items = [True]
items = {123}
# Ruff
items = [(True)]
items = [(True)]
items = {(123)}[Long lambda expressions](#long-lambda-expressions)
Both Ruff and Black will keep lambda parameters on a single line. However, if the body expression exceeds the configured line length, Ruff will additionally add parentheses around the lambda body and break it over multiple lines:
# Ruff
def a():
return b(
c,
d,
e,
f=lambda self, *args, **kwargs: (
baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs)
),
# More complex expressions also trigger wrapping
g=lambda self, *args, **kwargs: (
baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs) + 1
),
)
# Black
def a():
return b(
c,
d,
e,
f=lambda self, *args, **kwargs: baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
*args, **kwargs
),
# More complex expressions also trigger wrapping
g=lambda self, *args, **kwargs: baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
*args, **kwargs
)
+ 1,
)[Require single blank line between function and decorated class in stubs](#require-single-blank-line-between-function-and-decorated-class-in-stubs)
In stub files, Ruff enforces a single blank line between functions and decorated classes, whereas Black does not.
# Input and Ruff
def hello(): ...
@lambda _, /: _
class A: ...
# Black
def foo(): ...
@decorator
class Bar: ...[Avoiding additional space around escaped quote in triple-quoted docstring](#avoiding-additional-space-around-escaped-quote-in-triple-quoted-docstring)
In certain cases it is required to maintain a trailing space in a triple-quoted docstring in order to avoid escaping a quote or ending with too many quotes. However, if the last quote is escaped this is not necessary, and Ruff respects this. Black, however, adds the extra space.
# Input and Ruff
class Sample:
"""Hello "World\""""
def __init__(self, name):
self.name = name
# Black
class Sample:
"""Hello "World\" """
def __init__(self, name):
self.name = nameFile: site/ruff/installation/index.md
[Installing Ruff](#installing-ruff)
Ruff is available as ruff on PyPI.
Ruff can be invoked directly with uvx:
uvx ruff check # Lint all files in the current directory.
uvx ruff format # Format all files in the current directory.Or installed with uv (recommended), pip, or pipx:
$ # Install Ruff globally.
$ uv tool install ruff@latest
$ # Or add Ruff to your project.
$ uv add --dev ruff
$ # With pip.
$ pip install ruff
$ # With pipx.
$ pipx install ruffOnce installed, you can run Ruff from the command line:
$ ruff check # Lint all files in the current directory.
$ ruff format # Format all files in the current directory.Starting with version 0.5.0, Ruff can also be installed with our standalone installers:
$ # On macOS and Linux.
$ curl -LsSf https://astral.sh/ruff/install.sh | sh
$ # On Windows.
$ powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
$ # For a specific version.
$ curl -LsSf https://astral.sh/ruff/0.5.0/install.sh | sh
$ powershell -c "irm https://astral.sh/ruff/0.5.0/install.ps1 | iex"For macOS Homebrew and Linuxbrew users, Ruff is also available as ruff on Homebrew:
$ brew install ruffFor Conda users, Ruff is also available as ruff on conda-forge:
$ conda install -c conda-forge ruffFor pkgx users, Ruff is also available as ruff on the pkgx registry:
$ pkgx install ruffFor Arch Linux users, Ruff is also available as ruff on the official repositories:
$ pacman -S ruffFor Alpine users, Ruff is also available as ruff on the testing repositories:
$ apk add ruffFor openSUSE Tumbleweed users, Ruff is also available in the distribution repository:
$ sudo zypper install python3-ruffOn Docker, it is published as ghcr.io/astral-sh/ruff, tagged for each release and latest for the latest release.
$ docker run -v .:/io --rm ghcr.io/astral-sh/ruff check
$ docker run -v .:/io --rm ghcr.io/astral-sh/ruff:0.3.0 check
$ # Or, for Podman on SELinux.
$ docker run -v .:/io:Z --rm ghcr.io/astral-sh/ruff checkFile: site/ruff/integrations/index.md
[Integrations](#integrations)
[GitHub Actions](#github-actions)
GitHub Actions has everything you need to run Ruff out-of-the-box:
name: CI
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff
# Update output format to enable automatic inline annotations.
- name: Run Ruff
run: ruff check --output-format=github .Ruff can also be used as a GitHub Action via 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. 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).
To use ruff-action, create a file (e.g., .github/workflows/ruff.yml) inside your repository with:
name: Ruff
on: [ push, pull_request ]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3Alternatively, you can include ruff-action as a step in any other workflow file:
- uses: astral-sh/ruff-action@v3ruff-action accepts optional configuration parameters via with:, including:
version: The Ruff version to install (default: latest).args: The command-line arguments to pass to Ruff (default:"check").src: The source paths to pass to Ruff (default:[".", "src"]).
For example, to run ruff check --select B ./src using Ruff version 0.8.0:
- uses: astral-sh/ruff-action@v3
with:
version: 0.8.0
args: check --select B
src: "./src"[GitLab CI/CD](#gitlab-cicd)
You can add the following configuration to .gitlab-ci.yml to run a ruff format in parallel with a ruff check compatible with GitLab's codequality report.
.base_ruff:
stage: build
interruptible: true
image:
name: ghcr.io/astral-sh/ruff:0.15.16-alpine
before_script:
- cd $CI_PROJECT_DIR
- ruff --version
Ruff Check:
extends: .base_ruff
script:
- ruff check --output-format=gitlab --output-file=code-quality-report.json
artifacts:
reports:
codequality: $CI_PROJECT_DIR/code-quality-report.json
Ruff Format:
extends: .base_ruff
script:
- ruff format --diff[pre-commit](#pre-commit)
Ruff can be used as a pre-commit hook via ruff-pre-commit:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.15.16
hooks:
# Run the linter.
- id: ruff-check
# Run the formatter.
- id: ruff-formatTo enable lint fixes, add the --fix argument to the lint hook:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.15.16
hooks:
# Run the linter.
- id: ruff-check
args: [ --fix ]
# Run the formatter.
- id: ruff-formatTo avoid running on Jupyter Notebooks, remove jupyter from the list of allowed filetypes:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.15.16
hooks:
# Run the linter.
- id: ruff-check
types_or: [ python, pyi ]
args: [ --fix ]
# Run the formatter.
- id: ruff-format
types_or: [ python, pyi ]When running with --fix, Ruff's lint hook should be placed before Ruff's formatter hook, and before Black, isort, and other formatting tools, as Ruff's fix behavior can output code changes that require reformatting.
When running without --fix, Ruff's formatter hook can be placed before or after Ruff's lint hook.
(As long as your Ruff configuration avoids any linter-formatter incompatibilities, ruff format should never introduce new lint errors, so it's safe to run Ruff's format hook after ruff check --fix.)
[`mdformat`](#mdformat)
mdformat is capable of formatting code blocks within Markdown. The mdformat-ruff plugin enables mdformat to format Python code blocks with Ruff.
[Docker](#docker)
Ruff provides a distroless Docker image including the ruff binary. The following tags are published:
ruff:latestruff:{major}.{minor}.{patch}, e.g.,ruff:0.6.6ruff:{major}.{minor}, e.g.,ruff:0.6(the latest patch version)
In addition, ruff publishes the following images:
- Based on
alpine:3.20: ruff:alpineruff:alpine3.20- Based on
debian:bookworm-slim: ruff:debian-slimruff:bookworm-slim- Based on
buildpack-deps:bookworm: ruff:debianruff:bookworm
As with the distroless image, each image is published with ruff version tags as ruff:{major}.{minor}.{patch}-{base} and ruff:{major}.{minor}-{base}, e.g., ruff:0.6.6-alpine.
File: site/ruff/linter/index.md
[The Ruff Linter](#the-ruff-linter)
The Ruff Linter is an extremely fast Python linter designed as a drop-in replacement for Flake8 (plus dozens of plugins), isort, pydocstyle, pyupgrade, autoflake, and more.
[`ruff check`](#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. When linting a directory, Ruff searches for Python files recursively in that directory and all its subdirectories:
$ ruff check # Lint files in the current directory.
$ ruff check --fix # Lint files in the current directory and fix any fixable errors.
$ ruff check --watch # Lint files in the current directory and re-lint on change.
$ ruff check path/to/code/ # Lint files in `path/to/code`.For the full list of supported options, run ruff check --help.
[Rule selection](#rule-selection)
The set of enabled rules is controlled via the lint.select, lint.extend-select, and lint.ignore settings.
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 lint.select and lint.ignore accept either a full rule code (e.g., F401) or any valid prefix (e.g., F). For example, given the following configuration file:
[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"][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.
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
lint.selectoverlint.extend-selectto make your rule set explicit. - Use
ALLwith discretion. EnablingALLwill 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 toselect = ["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:
[tool.ruff.lint]
select = [
# pycodestyle
"E",
# Pyflakes
"F",
# pyupgrade
"UP",
# flake8-bugbear
"B",
# flake8-simplify
"SIM",
# isort
"I",
][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 lint.select and lint.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 extend-select and 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 configuration file:
[tool.ruff.lint]
select = ["E", "F"]
ignore = ["F401"][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](#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:
$ ruff check --fixBy default, Ruff will fix all violations for which safe fixes are available; to determine whether a rule supports fixing, see Rules.
[Fix safety](#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 change when applying unsafe fixes.
Specifically, an unsafe fix could lead to a change in runtime behavior, the removal of comments, or both, while safe fixes are intended to preserve runtime behavior and will only remove comments when deleting entire statements or expressions (e.g., removing unused imports).
For example, unnecessary-iterable-allocation-for-first-element (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:
$ python -m timeit "head = list(range(99999999))[0]"
1 loop, best of 5: 1.69 sec per loop$ python -m timeit "head = next(iter(range(99999999)))"
5000000 loops, best of 5: 70.8 nsec per loopHowever, when the collection is empty, this raised exception changes from an IndexError to StopIteration:
$ python -c 'list(range(0))[0]'
Traceback (most recent call last):
File "<string>", line 1, in <module>
IndexError: list index out of range$ python -c 'next(iter(range(0)))'
Traceback (most recent call last):
File "<string>", line 1, in <module>
StopIterationSince the change in exception type could break error handling upstream, this fix is categorized as unsafe.
Ruff only enables safe fixes by default. Unsafe fixes can be enabled by settings unsafe-fixes in your configuration file or passing the --unsafe-fixes flag to ruff check:
# Show unsafe fixes
ruff check --unsafe-fixes
# Apply unsafe fixes
ruff check --fix --unsafe-fixesBy default, Ruff will display a hint when unsafe fixes are available but not enabled. The suggestion can be silenced by setting the unsafe-fixes setting to false or using the --no-unsafe-fixes flag.
The safety of fixes can be adjusted per rule using the lint.extend-safe-fixes and lint.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:
[tool.ruff.lint]
extend-safe-fixes = ["F601"]
extend-unsafe-fixes = ["UP034"][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](#disabling-fixes)
To limit the set of rules that Ruff should fix, use the lint.fixable or lint.extend-fixable, and lint.unfixable settings.
For example, the following configuration would enable fixes for all rules except unused-imports (F401):
[tool.ruff.lint]
fixable = ["ALL"]
unfixable = ["F401"][lint]
fixable = ["ALL"]
unfixable = ["F401"]Conversely, the following configuration would only enable fixes for F401:
[tool.ruff.lint]
fixable = ["F401"][lint]
fixable = ["F401"][Error suppression](#error-suppression)
Ruff supports several mechanisms for suppressing lint errors, be they false positives or permissible violations.
[Configuration](#configuration)
To omit a lint rule everywhere, add it to the "ignore" list via the lint.ignore setting, either on the command-line or in your pyproject.toml or ruff.toml file.
To omit a lint rule within specific files based on file path prefixes or patterns, see the lint.per-file-ignores setting.
[Comments](#comments)
Ruff supports multiple forms of suppression comments, including inline and file-level noqa comments, and range suppressions.
[Line-level](#line-level)
Ruff supports a noqa system similar to Flake8. To ignore an individual violation, add # noqa: {code} to the end of the line, like so:
# Ignore F841.
x = 1 # noqa: F841
# Ignore E741 and F841.
i = 1 # noqa: E741, F841
# Ignore _all_ violations.
x = 1 # noqaFor 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:
"""Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.
""" # noqa: E501For import sorting, the noqa should come at the end of the first line in the import block, and will apply to all imports in the block, like so:
import os # noqa: I001
import abcThe full inline comment specification is as follows:
- An inline blanket
noqacomment is given by a case-insensitive match for#noqawith optional whitespace after the#symbol, followed by either: the end of the comment, the beginning of a new comment (#), or whitespace followed by any character other than:. - An inline
noqasuppression is given by first finding a case-insensitive match for#noqawith optional whitespace after the#symbol, optional whitespace afternoqa, and followed by the symbol:. After this we are expected to have a list of rule codes which is given by sequences of uppercase ASCII characters followed by ASCII digits, separated by whitespace or commas. The list ends at the last valid code. We will attempt to interpret rules with a missing delimiter (e.g.F401F841), though a warning will be emitted in this case.
The following is currently only available in preview mode.
To cover an entire "logical" line (a multi-line statement or suite header), an "ignore" comment may be placed above the first line:
# ruff: ignore[ARG001] # Covers the entire function signature
def foo(
arg1,
arg2,
):
pass
# ruff: ignore[E501] # Covers the entire list literal
things = [
"really long string literal ...",
"really long string literal ...",
]Alternately, placing the "ignore" comment inside of a multi-line statement, or at the end of a line, will cover only a single "physical" line, leaving the rest of the multi-line statement or header uncovered:
def foo(
arg1,
# ruff: ignore[ARG001] # Only covers `arg2`
arg2,
):
pass
things = [
"really long string literal ...", # ruff: ignore[E501] # Only covers this line
"really long string literal ...",
]Ignore comments can also be "stacked" with other comments or pragmas, and will still cover the next logical line:
# ruff: ignore[E741]
# ruff: ignore[F841]
# I definitely know what I'm doing.
i = 1The full line-level suppression comment specification is as follows:
- An own-line or trailing comment starting with case sensitive
#ruff:, with optional whitespace after the#symbol and:symbol, followed byignore[, any codes to be suppressed, and ending with]. - Codes to be suppressed must be separated by commas, with optional whitespace before or after each code, and may be followed by an optional trailing comma after the last code.
[Block-level](#block-level)
To ignore one or more violations within a range or block of code, a "disable" comment followed by a matching "enable" comment can be used, like so:
# ruff: disable[E501]
VALUE_1 = "Lorem ipsum dolor sit amet ..."
VALUE_2 = "Lorem ipsum dolor sit amet ..."
VALUE_3 = "Lorem ipsum dolor sit amet ..."
# ruff: enable[E501]To define a range, both the "disable" and "enable" comments must have matching codes, in the same order, as well as matching indentation levels within a logical block of code:
def foo():
# ruff: disable[E741, F841]
i = 1
# ruff: enable[E741, F841]If no matching "enable" comment is found, Ruff will also treat this as an "implicit" range. The implicit range is defined from the starting "disable" comment, until reaching a logical scope indented less than the starting comment:
def foo():
# ruff: disable[E741, F841]
i = 1
if True:
O = 1
l = 1
# implicit end of range
foo()It is strongly suggested to use explicit range suppressions, in order to prevent accidental suppressions of violations, especially at global module scope. For this reason, a RUF104 diagnostic will also be produced for any implicit range. If implicit range suppressions are desired, the RUF104 rule can be disabled, or an inline noqa suppression can be added to the end of the "disable" comment.
Range suppressions cannot be used to enable or select rules that aren't already selected by the project configuration or runtime flags. An "enable" comment can only be used to terminate a preceding "disable" comment with identical codes.
Unlike noqa suppressions, range suppressions do not support "blanket" suppression of all violations. At least one violation code must be listed.
The full range suppression comment specification is as follows:
- An own-line comment starting with case sensitive
#ruff:, with optional whitespace after the#symbol and:symbol, followed by eitherdisableorenableto start or end a range respectively, immediately followed by[, any codes to be suppressed, and ending with]. - Codes to be suppressed must be separated by commas, with optional whitespace before or after each code, and may be followed by an optional trailing comma after the last code.
[File-level](#file-level)
To ignore all violations across an entire file, add the line # ruff: noqa anywhere in the file, preferably towards the top, like so:
# ruff: noqaTo ignore a specific rule across an entire file, add the line # ruff: noqa: {code} anywhere in the file, preferably towards the top, like so:
# ruff: noqa: F841Global noqa comments must be on their own line to disambiguate from comments which ignore violations on a single line.
Note that Ruff will also respect Flake8's # flake8: noqa directive, and will treat it as equivalent to # ruff: noqa.
The file-level suppression comment specification is as follows:
- A file-level exemption comment is given by a case-sensitive match for
#ruff:or#flake8:, with optional whitespace after#and before:, followed by optional whitespace and a case-insensitive match fornoqa. After this, the specification is as in the inlinenoqasuppressions above.
In preview mode, one or more rules can be ignored across an entire file with a file-ignore comment on its own line, at global module scope, and preferably near the top of the file:
# ruff: file-ignore[F401, ARG001]The full-level suppression comment specification is as follows:
- An own-line comment starting with case sensitive
#ruff:, with optional whitespace after the#symbol and:symbol, followed byfile-ignore[, any codes to be suppressed, and ending with]. - Codes to be suppressed must be separated by commas, with optional whitespace before or after each code, and may be followed by an optional trailing comma after the last code.
[Detecting unused suppressions](#detecting-unused-suppressions)
Ruff implements a special rule, unused-noqa, under the RUF100 code, to enforce that your suppressions are "valid", in that the violations they say they ignore are actually being triggered and suppressed. To flag unused suppression comments, run Ruff with --extend-select RUF100, like so:
$ ruff check /path/to/file.py --extend-select RUF100Ruff can also remove any unused suppression comments via its fix functionality. To remove any unused suppressions, run Ruff with --fix, like so:
$ ruff check /path/to/file.py --extend-select RUF100 --fix[Inserting necessary suppression comments](#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 with --add-noqa, like so:
$ ruff check /path/to/file.py --add-noqa[isort action comments](#isort-action-comments)
Ruff respects isort's action comments (# 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.
Unlike isort, Ruff does not respect action comments within docstrings.
See the isort documentation for more.
[Exit codes](#exit-codes)
By default, ruff check exits with the following status codes:
0if no violations were found, or if all present violations were fixed automatically.1if violations were found.2if 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-zerowill cause Ruff to exit with a status code of0even if violations were found. Note that Ruff will still exit with a status code of2if it terminates abnormally.--exit-non-zero-on-fixwill cause Ruff to exit with a status code of1if violations were found, even if all such violations were fixed automatically. Note that the use of--exit-non-zero-on-fixcan result in a non-zero exit code even if no violations remain after fixing.
File: site/ruff/preview/index.md
[Preview](#preview)
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 unstable features such as new lint rules and fixes, formatter style changes, interface updates, and more. Warnings about deprecated features may turn into errors when using preview mode.
Enabling preview mode does not on its own enable all preview rules. See the rules section for details on selecting preview rules.
[Enabling preview mode](#enabling-preview-mode)
Preview mode can be enabled with the --preview flag on the CLI or by setting preview = true in your Ruff configuration file.
Preview mode can be configured separately for linting and formatting. To enable preview lint rules without preview style formatting:
[tool.ruff.lint]
preview = true[lint]
preview = trueruff check --previewTo enable preview style formatting without enabling any preview lint rules:
[tool.ruff.format]
preview = true[format]
preview = trueruff format --preview[Using rules that are in preview](#using-rules-that-are-in-preview)
If a rule is marked as preview, it can only be selected if preview mode is enabled. For example, consider a hypothetical rule, HYP001. If HYP001 were in preview, it would not be enabled by adding it to the selected rule set.
[tool.ruff.lint]
extend-select = ["HYP001"][lint]
extend-select = ["HYP001"]ruff check --extend-select HYP001It also would not be enabled by selecting the HYP category, like so:
[tool.ruff.lint]
extend-select = ["HYP"][lint]
extend-select = ["HYP"]ruff check --extend-select HYPSimilarly, it would not be enabled via the ALL selector:
[tool.ruff.lint]
select = ["ALL"][lint]
select = ["ALL"]ruff check --select ALLHowever, it would be enabled in any of the above cases if you enabled preview mode:
[tool.ruff.lint]
extend-select = ["HYP"]
preview = true[lint]
extend-select = ["HYP"]
preview = trueruff check --extend-select HYP --previewTo see which rules are currently in preview, visit the rules reference.
[Selecting single preview rules](#selecting-single-preview-rules)
When preview mode is enabled, selecting rule categories or prefixes will include all preview rules that match. If you'd prefer to opt in to each preview rule individually, you can toggle the explicit-preview-rules setting in your configuration file:
[tool.ruff.lint]
preview = true
explicit-preview-rules = true[lint]
preview = true
explicit-preview-rules = trueIn our previous example, --select with ALL HYP, HYP0, or HYP00 would not enable HYP001. Each preview rule will need to be selected with its exact code: for example, --select ALL,HYP001.
If preview mode is not enabled, this setting has no effect.
[Deprecated rules](#deprecated-rules)
When preview mode is enabled, deprecated rules will be disabled. If a deprecated rule is selected explicitly, an error will be raised. Deprecated rules will not be included if selected via a rule category or prefix.