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 checkruff 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.
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:
=== “pyproject.toml”
```toml [tool.ruff.lint] select = ["E", "F"] ignore = ["F401"] ```
=== “ruff.toml”
```toml [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:
lint.select over lint.extend-select to make your rule set explicit.ALL with discretion. Enabling ALL will implicitly enable new rules whenever you upgrade.select = ["E", "F"]) and add a group at-a-time. For example, you might consider expanding to select = ["E", "F", "B"] to enable the popular flake8-bugbear extension.For example, a configuration that enables some of the most popular rules (without being too pedantic) might look like the following:
=== “pyproject.toml”
```toml
[tool.ruff.lint]
select = [
# pycodestyle
"E",
# Pyflakes
"F",
# pyupgrade
"UP",
# flake8-bugbear
"B",
# flake8-simplify
"SIM",
# isort
"I",
]
```
=== “ruff.toml”
```toml
[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:
=== “pyproject.toml”
```toml [tool.ruff.lint] select = ["E", "F"] ignore = ["F401"] ```
=== “ruff.toml”
```toml [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.
When preview mode is enabled, rule selectors also accept the human-readable name of a rule (e.g., unused-import).
In preview, Ruff supports rule categories in addition to the Flake8-style linter groups described above. These categories organize rules by the types of issues they detect and determine whether rules are enabled by default. These categories and their descriptions, in order of decreasing severity, are:
noqa or ruff: ignore.correctness lints in that the code is likely wrong, but suspicious lints acknowledge that there are valid reasons for the code to be written in this way. You will still typically want to fix these issues, but using a suppression comment may occasionally be necessary. Deprecations generally also fit into this category.style or similar categories, but enforce styles that are too opinionated or are too prone to false positives to fit into another category.The first five categories compose the default rule set:
=== “pyproject.toml”
```toml
[tool.ruff.lint]
preview = true
select = [
"correctness",
"suspicious",
"complexity",
"performance",
"style",
]
```
=== “ruff.toml”
```toml
[lint]
preview = true
select = [
"correctness",
"suspicious",
"complexity",
"performance",
"style",
]
```
while the remaining four (security, formatting, pedantic, and restriction) are off by default. For certain projects, you may want to enable security as an entire category, but formatting, pedantic, and restriction contain a wider variety of opinionated lints, and you will typically only want to select individual rules from these categories directly.
See Trying out categories for more detailed steps on getting started.
Categories can be freely mixed with linter groups, linter prefixes, rule codes, and rule names. In addition to the priority relationships described above for settings like lint.select, lint.extend-select, and lint.ignore, and those for various configuration sources like pyproject.toml files and the CLI, the various selectors also have precedence relationships with each other. In general, you can think of this precedence as increasing from the broadest selector (ALL) to the narrowest single-rule selectors (e.g. F401 or unused-import):
ALL < category < linter group < linter prefix < rule
As shown above, this means that configuration like:
=== “pyproject.toml”
```toml [tool.ruff.lint] preview = true select = ["E", "F"] ignore = ["F401"] ```
=== “ruff.toml”
```toml [lint] preview = true select = ["E", "F"] ignore = ["F401"] ```
will select all E and F rules, with the exception of F401. Analogously, a selection with the suspicious category like:
=== “pyproject.toml”
```toml [tool.ruff.lint] preview = true select = ["suspicious"] ignore = ["UP"] ```
=== “ruff.toml”
```toml [lint] preview = true select = ["suspicious"] ignore = ["UP"] ```
would select all suspicious rules, except for the UP rules in that category.
This section is intended to help you choose which categories you want to enable, based on the rules and linter groups you have selected and on the types of issues you want to catch.
We expect virtually all projects to want the correctness rules enabled. The lints in this category include syntax errors that are not yet mapped to invalid-syntax diagnostics and other problems that cause immediate runtime errors. From there, suspicious is likely to be the next most helpful category. It includes rules that flag deprecated code, as well as classic footguns like mutable-argument-default (B006) that are almost always wrong but may be intentional in some cases. We tried to be conservative with the rules in correctness, so many rules like this that are only usually accurate are found in suspicious instead. In general, you should feel comfortable using a ruff: ignore comment on diagnostics from the suspicious or lower categories but think twice (or share feedback!) about suppressing a correctness lint.
The rules in the complexity, performance, and style categories are all stylistic, but we feel that these rules represent widely-accepted styles in the Python community. As demonstrated by their inclusion in the defaults, we expect most projects to want these rules enabled. Again, even if you enable these categories, you should feel comfortable ignoring certain rules project-wide or inline with suppression comments.
The security rules are focused on issues that may cause security vulnerabilities and overlap closely with the flake8-bandit (S) linter group. They are in their own category because these rules are intentionally biased toward false positives over false negatives and can be quite noisy. However, if your project is security-critical or just security-conscious, you will likely want to enable this entire category.
The formatting category contains rules that overlap with code formatters like the Ruff formatter or Black. If you use a code formatter, you will likely want to leave this category off. On the other hand, if you don't use a code formatter and rely on lint rules to enforce a consistent code format, you can select those rules from this category. Note that it contains rules beyond those related to PEP 8, however, so you may still want to select a subset of the formatting rules rather than the whole category.
pedantic rules, as you may guess, are pedantic, which can mean either “noisy,” leading to many diagnostics, or overly opinionated, suggesting changes that many Python users disagree with. Unlike the security category, you probably will not want to enable this category as a whole. Instead, we intend for rules from the pedantic category to be selected individually.
The restriction category goes beyond being pedantic to arbitrarily restrict even common code patterns, such as print (T201) or assert (S101). Like the pedantic category, we do not recommend enabling restriction as a whole. If you enable any restriction lints, they should be chosen narrowly for your project's needs.
If you‘re already using extend-select to extend the default rule set, you’ll inherit the category-based defaults automatically and won‘t need to modify your configuration. Similarly, if you’d like to try out the new categories without replacing your current configuration wholesale, the defaults, or a smaller subset like correctness and suspicious, are a great place to start. You can append them to an existing select configuration, or add them with extend-select. In either case, you shouldn‘t feel obligated to enable all of the new categories and can freely ignore the ones that don’t suit your needs.
If you run into any issues or have any suggestions about the new categories, please share any feedback in the tracking issue!
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 --fix
By default, Ruff will fix all violations for which safe fixes are available; to determine whether a rule supports fixing, see Rules.
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 loop
However, 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> StopIteration
Since 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-fixes
By 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:
=== “pyproject.toml”
```toml [tool.ruff.lint] extend-safe-fixes = ["F601"] extend-unsafe-fixes = ["UP034"] ```
=== “ruff.toml”
```toml [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.
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):
=== “pyproject.toml”
```toml [tool.ruff.lint] fixable = ["ALL"] unfixable = ["F401"] ```
=== “ruff.toml”
```toml [lint] fixable = ["ALL"] unfixable = ["F401"] ```
Conversely, the following configuration would only enable fixes for F401:
=== “pyproject.toml”
```toml [tool.ruff.lint] fixable = ["F401"] ```
=== “ruff.toml”
```toml [lint] fixable = ["F401"] ```
Ruff supports several mechanisms for suppressing lint errors, be they false positives or permissible violations.
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.
Ruff supports multiple forms of suppression comments, including inline and file-level noqa and ruff: ignore comments, and range suppressions.
In preview mode, rule names (e.g. unused-import) can be used in ruff: ignore, ruff: file-ignore, ruff: disable, and ruff: enable comments instead of rule codes (e.g. F401).
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 # noqa
For multi-line strings (like docstrings), the noqa directive should come at the end of the string (after the closing triple quote), and will apply to the entire string, like so:
"""Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor. """ # noqa: E501
For 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 abc
The full inline comment specification is as follows:
noqa comment is given by a case-insensitive match for #noqa with 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 :.noqa suppression is given by first finding a case-insensitive match for #noqa with optional whitespace after the # symbol, optional whitespace after noqa, 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.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 = 1
The full line-level suppression comment specification is as follows:
#ruff:, with optional whitespace after the # symbol and : symbol, followed by ignore[, any rules to be suppressed, and ending with ].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:
#ruff:, with optional whitespace after the # symbol and : symbol, followed by either disable or enable to start or end a range respectively, immediately followed by [, any codes to be suppressed, and ending with ].To ignore all violations across an entire file, add the line # ruff: noqa anywhere in the file, preferably towards the top, like so:
# ruff: noqa
To 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: F841
Global 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:
#ruff: or #flake8:, with optional whitespace after # and before :, followed by optional whitespace and a case-insensitive match for noqa. After this, the specification is as in the inline noqa suppressions above.One or more rules can also 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:
#ruff:, with optional whitespace after the # symbol and : symbol, followed by file-ignore[, any rules to be suppressed, and ending with ].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 RUF100
Ruff 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
Ruff can automatically add suppression comments to all lines that contain violations, which is useful when migrating a new codebase to Ruff. To add the appropriate comments to all relevant lines, run Ruff with --add-noqa to add noqa comments or with --add-ignore to add ruff: ignore comments:
$ ruff check /path/to/file.py --add-noqa $ ruff check /path/to/file.py --add-ignore
Both of these flags use rule codes on stable. To add ruff: ignore comments with human-readable rule names instead, use --add-ignore with preview mode enabled.
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.
By default, ruff check exits with the following status codes:
0 if no violations were found, or if all present violations were fixed automatically.1 if violations were found.2 if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an internal error.This convention mirrors that of tools like ESLint, Prettier, and RuboCop.
ruff check supports two command-line flags that alter its exit code behavior:
--exit-zero will cause Ruff to exit with a status code of 0 even if violations were found. Note that Ruff will still exit with a status code of 2 if it terminates abnormally.--exit-non-zero-on-fix will cause Ruff to exit with a status code of 1 if violations were found, even if all such violations were fixed automatically. Note that the use of --exit-non-zero-on-fix can result in a non-zero exit code even if no violations remain after fixing.