Add warning
3 files changed
tree: f95c5c98d8cbc654a328ceb78ae0710aa9e34f54
  1. .cargo/
  2. .github/
  3. assets/
  4. flake8_to_ruff/
  5. licenses/
  6. playground/
  7. python/
  8. resources/
  9. ruff_cli/
  10. ruff_dev/
  11. ruff_macros/
  12. scripts/
  13. src/
  14. .editorconfig
  15. .gitignore
  16. .pre-commit-config.yaml
  17. BREAKING_CHANGES.md
  18. Cargo.lock
  19. Cargo.toml
  20. CODE_OF_CONDUCT.md
  21. CONTRIBUTING.md
  22. LICENSE
  23. pyproject.toml
  24. README.md
  25. ruff.schema.json
  26. rust-toolchain
  27. rustfmt.toml
  28. setup.py
README.md

Ruff

Ruff image image image Actions status

An extremely fast Python linter, written in Rust.

  • ⚑️ 10-100x faster than existing linters
  • 🐍 Installable via pip
  • 🀝 Python 3.11 compatibility
  • πŸ› οΈ pyproject.toml support
  • πŸ“¦ Built-in caching, to avoid re-analyzing unchanged files
  • πŸ”§ Autofix support, for automatic error correction (e.g., automatically remove unused imports)
  • βš–οΈ Near-parity with the built-in Flake8 rule set
  • πŸ”Œ Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
  • ⌨️ First-party editor integrations for VS Code and more
  • 🌎 Monorepo-friendly, with hierarchical and cascading configuration

Ruff aims to be orders of magnitude faster than alternative tools while integrating more functionality behind a single, common interface.

Ruff can be used to replace Flake8 (plus a variety of plugins), isort, pydocstyle, yesqa, eradicate, pyupgrade, and autoflake, all while executing tens or hundreds of times faster than any individual tool.

Ruff goes beyond the responsibilities of a traditional linter, instead functioning as an advanced code transformation tool capable of upgrading type annotations, rewriting class definitions, sorting imports, and more.

Ruff is extremely actively developed and used in major open-source projects like:

Read the launch blog post.

Testimonials

SebastiΓ‘n RamΓ­rez, creator of FastAPI:

Ruff is so fast that sometimes I add an intentional bug in the code just to confirm it's actually running and checking the code.

Nick Schrock, founder of Elementl, co-creator of GraphQL:

Why is Ruff a gamechanger? Primarily because it is nearly 1000x faster. Literally. Not a typo. On our largest module (dagster itself, 250k LOC) pylint takes about 2.5 minutes, parallelized across 4 cores on my M1. Running ruff against our entire codebase takes .4 seconds.

Bryan Van de Ven, co-creator of Bokeh, original author of Conda:

Ruff is ~150-200x faster than flake8 on my machine, scanning the whole repo takes ~0.2s instead of ~20s. This is an enormous quality of life improvement for local dev. It's fast enough that I added it as an actual commit hook, which is terrific.

Timothy Crosley, creator of isort:

Just switched my first project to Ruff. Only one downside so far: it‘s so fast I couldn’t believe it was working till I intentionally introduced some errors.

Tim Abbott, lead developer of Zulip:

This is just ridiculously fast... ruff is amazing.

Table of Contents

  1. Installation and Usage
  2. Configuration
  3. Supported Rules
    1. Pyflakes (F)
    2. pycodestyle (E, W)
    3. mccabe (C90)
    4. isort (I)
    5. pydocstyle (D)
    6. pyupgrade (UP)
    7. pep8-naming (N)
    8. flake8-2020 (YTT)
    9. flake8-annotations (ANN)
    10. flake8-bandit (S)
    11. flake8-blind-except (BLE)
    12. flake8-boolean-trap (FBT)
    13. flake8-bugbear (B)
    14. flake8-builtins (A)
    15. flake8-comprehensions (C4)
    16. flake8-debugger (T10)
    17. flake8-errmsg (EM)
    18. flake8-implicit-str-concat (ISC)
    19. flake8-import-conventions (ICN)
    20. flake8-print (T20)
    21. flake8-pytest-style (PT)
    22. flake8-quotes (Q)
    23. flake8-return (RET)
    24. flake8-simplify (SIM)
    25. flake8-tidy-imports (TID)
    26. flake8-unused-arguments (ARG)
    27. flake8-datetimez (DTZ)
    28. eradicate (ERA)
    29. pandas-vet (PD)
    30. pygrep-hooks (PGH)
    31. Pylint (PL)
    32. flake8-pie (PIE)
    33. flake8-commas (COM)
    34. flake8-no-pep420 (INP)
    35. flake8-executable (EXE)
    36. flake8-type-checking (TYP)
    37. tryceratops (TRY)
    38. flake8-use-pathlib (PTH)
    39. Ruff-specific rules (RUF)
  4. Editor Integrations
  5. FAQ
  6. Contributing
  7. Releases
  8. Benchmarks
  9. Reference
  10. License

Installation and Usage

Installation

Ruff is available as ruff on PyPI:

pip install ruff

For macOS Homebrew and Linuxbrew users, Ruff is also available as ruff on Homebrew:

brew install ruff

For Conda users, Ruff is also available as ruff on conda-forge:

conda install -c conda-forge ruff

For Arch Linux users, Ruff is also available as ruff on the official repositories:

pacman -S ruff

Packaging status

Usage

To run Ruff, try any of the following:

ruff path/to/code/to/lint.py  # Run Ruff over `lint.py`
ruff path/to/code/            # Run Ruff over all files in `/path/to/code` (and any subdirectories)
ruff path/to/code/*.py        # Run Ruff over all `.py` files in `/path/to/code`

You can run Ruff in --watch mode to automatically re-run on-change:

ruff path/to/code/ --watch

Ruff also works with pre-commit:

- repo: https://github.com/charliermarsh/ruff-pre-commit
  # Ruff version.
  rev: 'v0.0.231'
  hooks:
    - id: ruff

Configuration

Ruff is configurable both via pyproject.toml and the command line. For a full list of configurable options, see the API reference.

If left unspecified, the default configuration is equivalent to:

[tool.ruff]
line-length = 88

# Enable Pyflakes `E` and `F` codes by default.
select = ["E", "F"]
ignore = []

# Exclude a variety of commonly ignored directories.
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
]
per-file-ignores = {}

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

# Assume Python 3.10.
target-version = "py310"

[tool.ruff.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10

As an example, the following would configure Ruff to: (1) avoid enforcing line-length violations (E501); (2) never remove unused imports (F401); and (3) ignore import-at-top-of-file violations (E402) in __init__.py files:

[tool.ruff]
# Enable Pyflakes and pycodestyle rules.
select = ["E", "F"]

# Never enforce `E501` (line length violations).
ignore = ["E501"]

# Never try to fix `F401` (unused imports).
unfixable = ["F401"]

# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
[tool.ruff.per-file-ignores]
"__init__.py" = ["E402"]
"path/to/file.py" = ["E402"]

Plugin configurations should be expressed as subsections, e.g.:

[tool.ruff]
# Add "Q" to the list of enabled codes.
select = ["E", "F", "Q"]

[tool.ruff.flake8-quotes]
docstring-quotes = "double"

Ruff mirrors Flake8's rule code system, in which each rule code consists of a one-to-three letter prefix, followed by three digits (e.g., F401). The prefix indicates that “source” of the rule (e.g., F for Pyflakes, E for pycodestyle, ANN for flake8-annotations). The set of enabled rules is determined by the select and ignore options, which support both the full code (e.g., F401) and the prefix (e.g., F).

As a special-case, Ruff also supports the ALL code, which enables all rules. Note that some of the pydocstyle rules conflict (e.g., D203 and D211) as they represent alternative docstring formats. Enabling ALL without further configuration may result in suboptimal behavior, especially for the pydocstyle plugin.

As an alternative to pyproject.toml, Ruff will also respect a ruff.toml file, which implements an equivalent schema (though the [tool.ruff] hierarchy can be omitted). For example, the pyproject.toml described above would be represented via the following ruff.toml:

# Enable Pyflakes and pycodestyle rules.
select = ["E", "F"]

# Never enforce `E501` (line length violations).
ignore = ["E501"]

# Always autofix, but never try to fix `F401` (unused imports).
fix = true
unfixable = ["F401"]

# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
[per-file-ignores]
"__init__.py" = ["E402"]
"path/to/file.py" = ["E402"]

For a full list of configurable options, see the API reference.

Some common configuration settings can be provided via the command-line:

ruff path/to/code/ --select F401 --select F403

See ruff --help for more:

Ruff: An extremely fast Python linter.

Usage: ruff [OPTIONS] [FILES]...

Arguments:
  [FILES]...

Options:
      --config <CONFIG>
          Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
  -v, --verbose
          Enable verbose logging
  -q, --quiet
          Print lint violations, but nothing else
  -s, --silent
          Disable all logging (but still exit with status code "1" upon detecting lint violations)
  -e, --exit-zero
          Exit with status code "0", even upon detecting lint violations
  -w, --watch
          Run in watch mode by re-running whenever files change
      --fix
          Attempt to automatically fix lint violations
      --fix-only
          Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`
      --diff
          Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
  -n, --no-cache
          Disable cache reads
      --isolated
          Ignore all configuration files
      --select <RULE_CODE>
          Comma-separated list of rule codes to enable (or ALL, to enable all rules)
      --extend-select <RULE_CODE>
          Like --select, but adds additional rule codes on top of the selected ones
      --ignore <RULE_CODE>
          Comma-separated list of rule codes to disable
      --extend-ignore <RULE_CODE>
          Like --ignore, but adds additional rule codes on top of the ignored ones
      --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
      --fixable <RULE_CODE>
          List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
      --unfixable <RULE_CODE>
          List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
      --per-file-ignores <PER_FILE_IGNORES>
          List of mappings from file pattern to code to exclude
      --format <FORMAT>
          Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint]
      --stdin-filename <STDIN_FILENAME>
          The name of the file when passing it through stdin
      --cache-dir <CACHE_DIR>
          Path to the cache directory [env: RUFF_CACHE_DIR=]
      --show-source
          Show violations with source code
      --respect-gitignore
          Respect file exclusions via `.gitignore` and other standard ignore files
      --force-exclude
          Enforce exclusions, even for paths passed to Ruff directly on the command-line
      --update-check
          Enable or disable automatic update checks
      --dummy-variable-rgx <DUMMY_VARIABLE_RGX>
          Regular expression matching the name of dummy variables
      --target-version <TARGET_VERSION>
          The minimum Python version that should be supported
      --line-length <LINE_LENGTH>
          Set the line-length for length-associated rules and automatic formatting
      --add-noqa
          Enable automatic additions of `noqa` directives to failing lines
      --clean
          Clear any caches in the current directory or any subdirectories
      --explain <EXPLAIN>
          Explain a rule
      --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 information
  -V, --version
          Print version information

pyproject.toml discovery

Similar to ESLint, Ruff supports hierarchical configuration, such that the “closest” pyproject.toml file in the directory hierarchy is used for every individual file, with all paths in the pyproject.toml file (e.g., exclude globs, src paths) being resolved relative to the directory containing the pyproject.toml file.

There are a few exceptions to these rules:

  1. In locating the “closest” pyproject.toml file for a given path, Ruff ignores any pyproject.toml files that lack a [tool.ruff] section.
  2. If a configuration file is passed directly via --config, those settings are used for across files. Any relative paths in that configuration file (like exclude globs or src paths) are resolved relative to the current working directory.
  3. If no pyproject.toml file is found in the filesystem hierarchy, Ruff will fall back to using a default configuration. If a user-specific configuration file exists at ${config_dir}/ruff/pyproject.toml, that file will be used instead of the default configuration, with ${config_dir} being determined via the dirs crate, and all relative paths being again resolved relative to the current working directory.
  4. Any pyproject.toml-supported settings that are provided on the command-line (e.g., via --select) will override the settings in every resolved configuration file.

Unlike ESLint, 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 pyproject.toml file, like so:

# Extend the `pyproject.toml` file in the parent directory.
extend = "../pyproject.toml"
# But use a different line length.
line-length = 100

All of the above rules apply equivalently to ruff.toml files. If Ruff detects both a ruff.toml and pyproject.toml file, it will defer to the ruff.toml.

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 pyproject.toml file.

By default, Ruff will also skip any files that are omitted via .ignore, .gitignore, .git/info/exclude, and global gitignore files (see: respect-gitignore).

Files that are passed to ruff directly are always linted, regardless of the above criteria. For example, ruff /path/to/excluded/file.py will always lint file.py.

Ignoring errors

To omit a lint rule entirely, add it to the “ignore” list via ignore or extend-ignore, either on the command-line or in your pyproject.toml file.

To ignore a violation inline, Ruff uses a noqa system similar to Flake8. 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

Note that, for multi-line strings, the noqa directive should come at the end of the string, 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 incididunt ut labore et dolore magna aliqua.
"""  # noqa: E501

To ignore all violations across an entire file, Ruff supports Flake8's # flake8: noqa directive (or, equivalently, # ruff: noqa). Adding either of those directives to any part of a file will disable enforcement across the entire file.

For targeted exclusions across entire files (e.g., “Ignore all F841 violations in /path/to/file.py”), see the per-file-ignores configuration setting.

“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.

See the isort documentation for more.

Automating noqa Directives

Ruff supports several workflows to aid in noqa management.

First, Ruff provides a special rule code, RUF100, to enforce that your noqa directives are “valid”, in that the violations they say they ignore are actually being triggered on that line (and thus suppressed). You can run ruff /path/to/file.py --extend-select RUF100 to flag unused noqa directives.

Second, Ruff can automatically remove unused noqa directives via its autofix functionality. You can run ruff /path/to/file.py --extend-select RUF100 --fix to automatically remove unused noqa directives.

Third, Ruff can automatically add noqa directives to all failing lines. This is useful when migrating a new codebase to Ruff. You can run ruff /path/to/file.py --add-noqa to automatically add noqa directives to all failing lines, with the appropriate rule codes.

Supported Rules

Regardless of the rule's origin, Ruff re-implements every rule in Rust as a first-party feature.

By default, Ruff enables all E and F rule codes, which correspond to those built-in to Flake8.

The πŸ›  emoji indicates that a rule is automatically fixable by the --fix command-line option.

Pyflakes (F)

For more, see Pyflakes on PyPI.

CodeNameMessageFix
F401unused-import{name} imported but unused; consider adding to __all__ or using a redundant aliasπŸ› 
F402import-shadowed-by-loop-varImport {name} from line {line} shadowed by loop variable
F403import-star-usedfrom {name} import * used; unable to detect undefined names
F404late-future-importfrom __future__ imports must occur at the beginning of the file
F405import-star-usage{name} may be undefined, or defined from star imports: {sources}
F406import-star-not-permittedfrom {name} import * only allowed at module level
F407future-feature-not-definedFuture feature {name} is not defined
F501percent-format-invalid-format‘...’ % ... has invalid format string: {message}
F502percent-format-expected-mapping‘...’ % ... expected mapping but got sequence
F503percent-format-expected-sequence‘...’ % ... expected sequence but got mapping
F504percent-format-extra-named-arguments‘...’ % ... has unused named argument(s): {message}πŸ› 
F505percent-format-missing-argument‘...’ % ... is missing argument(s) for placeholder(s): {message}
F506percent-format-mixed-positional-and-named‘...’ % ... has mixed positional and named placeholders
F507percent-format-positional-count-mismatch‘...’ % ... has {wanted} placeholder(s) but {got} substitution(s)
F508percent-format-star-requires-sequence‘...’ % ... * specifier requires sequence
F509percent-format-unsupported-format-character‘...’ % ... has unsupported format character ‘{char}’
F521string-dot-format-invalid-format‘...’.format(...) has invalid format string: {message}
F522string-dot-format-extra-named-arguments‘...’.format(...) has unused named argument(s): {message}πŸ› 
F523string-dot-format-extra-positional-arguments‘...’.format(...) has unused arguments at position(s): {message}
F524string-dot-format-missing-arguments‘...’.format(...) is missing argument(s) for placeholder(s): {message}
F525string-dot-format-mixing-automatic‘...’.format(...) mixes automatic and manual numbering
F541f-string-missing-placeholdersf-string without any placeholdersπŸ› 
F601multi-value-repeated-key-literalDictionary key literal {name} repeatedπŸ› 
F602multi-value-repeated-key-variableDictionary key {name} repeatedπŸ› 
F621expressions-in-star-assignmentToo many expressions in star-unpacking assignment
F622two-starred-expressionsTwo starred expressions in assignment
F631assert-tupleAssert test is a non-empty tuple, which is always True
F632is-literalUse == to compare constant literalsπŸ› 
F633invalid-print-syntaxUse of >> is invalid with print function
F634if-tupleIf test is a tuple, which is always True
F701break-outside-loopbreak outside loop
F702continue-outside-loopcontinue not properly in loop
F704yield-outside-function{keyword} statement outside of a function
F706return-outside-functionreturn statement outside of a function/method
F707default-except-not-lastAn except block as not the last exception handler
F722forward-annotation-syntax-errorSyntax error in forward annotation: {body}
F811redefined-while-unusedRedefinition of unused {name} from line {line}
F821undefined-nameUndefined name {name}
F822undefined-exportUndefined name {name} in __all__
F823undefined-localLocal variable {name} referenced before assignment
F841unused-variableLocal variable {name} is assigned to but never usedπŸ› 
F842unused-annotationLocal variable {name} is annotated but never used
F901raise-not-implementedraise NotImplemented should be raise NotImplementedErrorπŸ› 

pycodestyle (E, W)

For more, see pycodestyle on PyPI.

Error (E)

CodeNameMessageFix
E101mixed-spaces-and-tabsIndentation contains mixed spaces and tabs
E401multiple-imports-on-one-lineMultiple imports on one line
E402module-import-not-at-top-of-fileModule level import not at top of file
E501line-too-longLine too long ({length} > {limit} characters)
E711none-comparisonComparison to None should be cond is NoneπŸ› 
E712true-false-comparisonComparison to True should be cond is TrueπŸ› 
E713not-in-testTest for membership should be not inπŸ› 
E714not-is-testTest for object identity should be is notπŸ› 
E721type-comparisonDo not compare types, use isinstance()
E722do-not-use-bare-exceptDo not use bare except
E731do-not-assign-lambdaDo not assign a lambda expression, use a defπŸ› 
E741ambiguous-variable-nameAmbiguous variable name: {name}
E742ambiguous-class-nameAmbiguous class name: {name}
E743ambiguous-function-nameAmbiguous function name: {name}
E902io-error{message}
E999syntax-errorSyntaxError: {message}

Warning (W)

CodeNameMessageFix
W292no-new-line-at-end-of-fileNo newline at end of fileπŸ› 
W505doc-line-too-longDoc line too long ({length} > {limit} characters)
W605invalid-escape-sequenceInvalid escape sequence: ‘{char}’πŸ› 

mccabe (C90)

For more, see mccabe on PyPI.

CodeNameMessageFix
C901function-is-too-complex{name} is too complex ({complexity})

isort (I)

For more, see isort on PyPI.

CodeNameMessageFix
I001unsorted-importsImport block is un-sorted or un-formattedπŸ› 
I002missing-required-importMissing required import: {name}πŸ› 

pydocstyle (D)

For more, see pydocstyle on PyPI.

CodeNameMessageFix
D100public-moduleMissing docstring in public module
D101public-classMissing docstring in public class
D102public-methodMissing docstring in public method
D103public-functionMissing docstring in public function
D104public-packageMissing docstring in public package
D105magic-methodMissing docstring in magic method
D106public-nested-classMissing docstring in public nested class
D107public-initMissing docstring in __init__
D200fits-on-one-lineOne-line docstring should fit on one lineπŸ› 
D201no-blank-line-before-functionNo blank lines allowed before function docstring (found {num_lines})πŸ› 
D202no-blank-line-after-functionNo blank lines allowed after function docstring (found {num_lines})πŸ› 
D203one-blank-line-before-class1 blank line required before class docstringπŸ› 
D204one-blank-line-after-class1 blank line required after class docstringπŸ› 
D205blank-line-after-summary1 blank line required between summary line and descriptionπŸ› 
D206indent-with-spacesDocstring should be indented with spaces, not tabs
D207no-under-indentationDocstring is under-indentedπŸ› 
D208no-over-indentationDocstring is over-indentedπŸ› 
D209new-line-after-last-paragraphMulti-line docstring closing quotes should be on a separate lineπŸ› 
D210no-surrounding-whitespaceNo whitespaces allowed surrounding docstring textπŸ› 
D211no-blank-line-before-classNo blank lines allowed before class docstringπŸ› 
D212multi-line-summary-first-lineMulti-line docstring summary should start at the first line
D213multi-line-summary-second-lineMulti-line docstring summary should start at the second line
D214section-not-over-indentedSection is over-indented (“{name}”)πŸ› 
D215section-underline-not-over-indentedSection underline is over-indented (“{name}”)πŸ› 
D300uses-triple-quotesUse “““triple double quotes”””
D301uses-r-prefix-for-backslashed-contentUse r""" if any backslashes in a docstring
D400ends-in-periodFirst line should end with a periodπŸ› 
D401non-imperative-moodFirst line of docstring should be in imperative mood: “{first_line}”
D402no-signatureFirst line should not be the function's signature
D403first-line-capitalizedFirst word of the first line should be properly capitalized
D404no-this-prefixFirst word of the docstring should not be “This”
D405capitalize-section-nameSection name should be properly capitalized (“{name}”)πŸ› 
D406new-line-after-section-nameSection name should end with a newline (“{name}”)πŸ› 
D407dashed-underline-after-sectionMissing dashed underline after section (“{name}”)πŸ› 
D408section-underline-after-nameSection underline should be in the line following the section's name (“{name}”)πŸ› 
D409section-underline-matches-section-lengthSection underline should match the length of its name (“{name}”)πŸ› 
D410blank-line-after-sectionMissing blank line after section (“{name}”)πŸ› 
D411blank-line-before-sectionMissing blank line before section (“{name}”)πŸ› 
D412no-blank-lines-between-header-and-contentNo blank lines allowed between a section header and its content (“{name}”)πŸ› 
D413blank-line-after-last-sectionMissing blank line after last section (“{name}”)πŸ› 
D414non-empty-sectionSection has no content (“{name}”)
D415ends-in-punctuationFirst line should end with a period, question mark, or exclamation pointπŸ› 
D416section-name-ends-in-colonSection name should end with a colon (“{name}”)πŸ› 
D417document-all-argumentsMissing argument description in the docstring: {name}
D418skip-docstringFunction decorated with @overload shouldn't contain a docstring
D419non-emptyDocstring is empty

pyupgrade (UP)

For more, see pyupgrade on PyPI.

CodeNameMessageFix
UP001useless-metaclass-type__metaclass__ = type is impliedπŸ› 
UP003type-of-primitiveUse {} instead of type(...)πŸ› 
UP004useless-object-inheritanceClass {name} inherits from objectπŸ› 
UP005deprecated-unittest-alias{alias} is deprecated, use {target}πŸ› 
UP006use-pep585-annotationUse {} instead of {} for type annotationsπŸ› 
UP007use-pep604-annotationUse X | Y for type annotationsπŸ› 
UP008super-call-with-parametersUse super() instead of super(__class__, self)πŸ› 
UP009pep3120-unnecessary-coding-commentUTF-8 encoding declaration is unnecessaryπŸ› 
UP010unnecessary-future-importUnnecessary __future__ import {import} for target Python versionπŸ› 
UP011lru-cache-without-parametersUnnecessary parameters to functools.lru_cacheπŸ› 
UP012unnecessary-encode-utf8Unnecessary call to encode as UTF-8πŸ› 
UP013convert-typed-dict-functional-to-classConvert {name} from TypedDict functional to class syntaxπŸ› 
UP014convert-named-tuple-functional-to-classConvert {name} from NamedTuple functional to class syntaxπŸ› 
UP015redundant-open-modesUnnecessary open mode parametersπŸ› 
UP016remove-six-compatUnnecessary six compatibility usageπŸ› 
UP017datetime-timezone-utcUse datetime.UTC aliasπŸ› 
UP018native-literalsUnnecessary call to {literal_type}πŸ› 
UP019typing-text-str-aliastyping.Text is deprecated, use strπŸ› 
UP020open-aliasUse builtin openπŸ› 
UP021replace-universal-newlinesuniversal_newlines is deprecated, use textπŸ› 
UP022replace-stdout-stderrSending stdout and stderr to pipe is deprecated, use capture_outputπŸ› 
UP023rewrite-c-element-treecElementTree is deprecated, use ElementTreeπŸ› 
UP024os-error-aliasReplace aliased errors with OSErrorπŸ› 
UP025rewrite-unicode-literalRemove unicode literals from stringsπŸ› 
UP026rewrite-mock-importmock is deprecated, use unittest.mockπŸ› 
UP027rewrite-list-comprehensionReplace unpacked list comprehension with a generator expressionπŸ› 
UP028rewrite-yield-fromReplace yield over for loop with yield fromπŸ› 
UP029unnecessary-builtin-importUnnecessary builtin import: {import}πŸ› 
UP030format-literalsUse implicit references for positional format fieldsπŸ› 
UP031printf-string-formattingUse format specifiers instead of percent formatπŸ› 
UP032f-stringUse f-string instead of format callπŸ› 
UP033functools-cacheUse @functools.cache instead of @functools.lru_cache(maxsize=None)πŸ› 
UP034extraneous-parenthesesAvoid extraneous parenthesesπŸ› 

pep8-naming (N)

For more, see pep8-naming on PyPI.

CodeNameMessageFix
N801invalid-class-nameClass name {name} should use CapWords convention
N802invalid-function-nameFunction name {name} should be lowercase
N803invalid-argument-nameArgument name {name} should be lowercase
N804invalid-first-argument-name-for-class-methodFirst argument of a class method should be named cls
N805invalid-first-argument-name-for-methodFirst argument of a method should be named self
N806non-lowercase-variable-in-functionVariable {name} in function should be lowercase
N807dunder-function-nameFunction name should not start and end with __
N811constant-imported-as-non-constantConstant {name} imported as non-constant {asname}
N812lowercase-imported-as-non-lowercaseLowercase {name} imported as non-lowercase {asname}
N813camelcase-imported-as-lowercaseCamelcase {name} imported as lowercase {asname}
N814camelcase-imported-as-constantCamelcase {name} imported as constant {asname}
N815mixed-case-variable-in-class-scopeVariable {name} in class scope should not be mixedCase
N816mixed-case-variable-in-global-scopeVariable {name} in global scope should not be mixedCase
N817camelcase-imported-as-acronymCamelcase {name} imported as acronym {asname}
N818error-suffix-on-exception-nameException name {name} should be named with an Error suffix

flake8-2020 (YTT)

For more, see flake8-2020 on PyPI.

CodeNameMessageFix
YTT101sys-version-slice3-referencedsys.version[:3] referenced (python3.10), use sys.version_info
YTT102sys-version2-referencedsys.version[2] referenced (python3.10), use sys.version_info
YTT103sys-version-cmp-str3sys.version compared to string (python3.10), use sys.version_info
YTT201sys-version-info0-eq3-referencedsys.version_info[0] == 3 referenced (python4), use >=
YTT202six-py3-referencedsix.PY3 referenced (python4), use not six.PY2
YTT203sys-version-info1-cmp-intsys.version_info[1] compared to integer (python4), compare sys.version_info to tuple
YTT204sys-version-info-minor-cmp-intsys.version_info.minor compared to integer (python4), compare sys.version_info to tuple
YTT301sys-version0-referencedsys.version[0] referenced (python10), use sys.version_info
YTT302sys-version-cmp-str10sys.version compared to string (python10), use sys.version_info
YTT303sys-version-slice1-referencedsys.version[:1] referenced (python10), use sys.version_info

flake8-annotations (ANN)

For more, see flake8-annotations on PyPI.

CodeNameMessageFix
ANN001missing-type-function-argumentMissing type annotation for function argument {name}
ANN002missing-type-argsMissing type annotation for *{name}
ANN003missing-type-kwargsMissing type annotation for **{name}
ANN101missing-type-selfMissing type annotation for {name} in method
ANN102missing-type-clsMissing type annotation for {name} in classmethod
ANN201missing-return-type-public-functionMissing return type annotation for public function {name}
ANN202missing-return-type-private-functionMissing return type annotation for private function {name}
ANN204missing-return-type-special-methodMissing return type annotation for special method {name}πŸ› 
ANN205missing-return-type-static-methodMissing return type annotation for staticmethod {name}
ANN206missing-return-type-class-methodMissing return type annotation for classmethod {name}
ANN401dynamically-typed-expressionDynamically typed expressions (typing.Any) are disallowed in {name}

flake8-bandit (S)

For more, see flake8-bandit on PyPI.

CodeNameMessageFix
S101assert-usedUse of assert detected
S102exec-usedUse of exec detected
S103bad-file-permissionsos.chmod setting a permissive mask {mask:#o} on file or directory
S104hardcoded-bind-all-interfacesPossible binding to all interfaces
S105hardcoded-password-stringPossible hardcoded password: “{}”
S106hardcoded-password-func-argPossible hardcoded password: “{}”
S107hardcoded-password-defaultPossible hardcoded password: “{}”
S108hardcoded-temp-fileProbable insecure usage of temporary file or directory: “{}”
S113request-without-timeoutProbable use of requests call with timeout set to {value}
S324hashlib-insecure-hash-functionProbable use of insecure hash functions in hashlib: “{}”
S501request-with-no-cert-validationProbable use of {string} call with verify=False disabling SSL certificate checks
S506unsafe-yaml-loadProbable use of unsafe loader {name} with yaml.load. Allows instantiation of arbitrary objects. Consider yaml.safe_load.
S508snmp-insecure-versionThe use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able.
S509snmp-weak-cryptographyYou should not use SNMPv3 without encryption. noAuthNoPriv & authNoPriv is insecure.
S612logging-config-insecure-listenUse of insecure logging.config.listen detected
S701jinja2-autoescape-falseUsing jinja2 templates with autoescape=False is dangerous and can lead to XSS. Ensure autoescape=True or use the select_autoescape function.

flake8-blind-except (BLE)

For more, see flake8-blind-except on PyPI.

CodeNameMessageFix
BLE001blind-exceptDo not catch blind exception: {name}

flake8-boolean-trap (FBT)

For more, see flake8-boolean-trap on PyPI.

CodeNameMessageFix
FBT001boolean-positional-arg-in-function-definitionBoolean positional arg in function definition
FBT002boolean-default-value-in-function-definitionBoolean default value in function definition
FBT003boolean-positional-value-in-function-callBoolean positional value in function call

flake8-bugbear (B)

For more, see flake8-bugbear on PyPI.

CodeNameMessageFix
B002unary-prefix-incrementPython does not support the unary prefix increment
B003assignment-to-os-environAssigning to os.environ doesn't clear the environment
B004unreliable-callable-checkUsing hasattr(x, '__call__') to test if x is callable is unreliable. Use callable(x) for consistent results.
B005strip-with-multi-charactersUsing .strip() with multi-character strings is misleading the reader
B006mutable-argument-defaultDo not use mutable data structures for argument defaults
B007unused-loop-control-variableLoop control variable {name} not used within the loop bodyπŸ› 
B008function-call-argument-defaultDo not perform function call {name} in argument defaults
B009get-attr-with-constantDo not call getattr with a constant attribute value. It is not any safer than normal property access.πŸ› 
B010set-attr-with-constantDo not call setattr with a constant attribute value. It is not any safer than normal property access.πŸ› 
B011do-not-assert-falseDo not assert False (python -O removes these calls), raise AssertionError()πŸ› 
B012jump-statement-in-finally{name} inside finally blocks cause exceptions to be silenced
B013redundant-tuple-in-exception-handlerA length-one tuple literal is redundant. Write except {name} instead of except ({name},).πŸ› 
B014duplicate-handler-exceptionException handler with duplicate exception: {name}πŸ› 
B015useless-comparisonPointless comparison. This comparison does nothing but waste CPU instructions. Either prepend assert or remove it.
B016cannot-raise-literalCannot raise a literal. Did you intend to return it or raise an Exception?
B017no-assert-raises-exceptionassertRaises(Exception) should be considered evil
B018useless-expressionFound useless expression. Either assign it to a variable or remove it.
B019cached-instance-methodUse of functools.lru_cache or functools.cache on methods can lead to memory leaks
B020loop-variable-overrides-iteratorLoop control variable {name} overrides iterable it iterates
B021f-string-docstringf-string used as docstring. This will be interpreted by python as a joined string rather than a docstring.
B022useless-contextlib-suppressNo arguments passed to contextlib.suppress. No exceptions will be suppressed and therefore this context manager is redundant
B023function-uses-loop-variableFunction definition does not bind loop variable {name}
B024abstract-base-class-without-abstract-method{name} is an abstract base class, but it has no abstract methods
B025duplicate-try-block-exceptiontry-except block with duplicate exception {name}
B026star-arg-unpacking-after-keyword-argStar-arg unpacking after a keyword argument is strongly discouraged
B027empty-method-without-abstract-decorator{name} is an empty method in an abstract base class, but has no abstract decorator
B904raise-without-from-inside-exceptWithin an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
B905zip-without-explicit-strictzip() without an explicit strict= parameter

flake8-builtins (A)

For more, see flake8-builtins on PyPI.

CodeNameMessageFix
A001builtin-variable-shadowingVariable {name} is shadowing a python builtin
A002builtin-argument-shadowingArgument {name} is shadowing a python builtin
A003builtin-attribute-shadowingClass attribute {name} is shadowing a python builtin

flake8-comprehensions (C4)

For more, see flake8-comprehensions on PyPI.

CodeNameMessageFix
C400unnecessary-generator-listUnnecessary generator (rewrite as a list comprehension)πŸ› 
C401unnecessary-generator-setUnnecessary generator (rewrite as a set comprehension)πŸ› 
C402unnecessary-generator-dictUnnecessary generator (rewrite as a dict comprehension)πŸ› 
C403unnecessary-list-comprehension-setUnnecessary list comprehension (rewrite as a set comprehension)πŸ› 
C404unnecessary-list-comprehension-dictUnnecessary list comprehension (rewrite as a dict comprehension)πŸ› 
C405unnecessary-literal-setUnnecessary {obj_type} literal (rewrite as a set literal)πŸ› 
C406unnecessary-literal-dictUnnecessary {obj_type} literal (rewrite as a dict literal)πŸ› 
C408unnecessary-collection-callUnnecessary {obj_type} call (rewrite as a literal)πŸ› 
C409unnecessary-literal-within-tuple-callUnnecessary {literal} literal passed to tuple() (rewrite as a tuple literal)πŸ› 
C410unnecessary-literal-within-list-callUnnecessary {literal} literal passed to list() (remove the outer call to list())πŸ› 
C411unnecessary-list-callUnnecessary list call (remove the outer call to list())πŸ› 
C413unnecessary-call-around-sortedUnnecessary {func} call around sorted()πŸ› 
C414unnecessary-double-cast-or-processUnnecessary {inner} call within {outer}()
C415unnecessary-subscript-reversalUnnecessary subscript reversal of iterable within {func}()
C416unnecessary-comprehensionUnnecessary {obj_type} comprehension (rewrite using {obj_type}())πŸ› 
C417unnecessary-mapUnnecessary map usage (rewrite using a generator expression)

flake8-debugger (T10)

For more, see flake8-debugger on PyPI.

CodeNameMessageFix
T100debuggerTrace found: {name} used

flake8-errmsg (EM)

For more, see flake8-errmsg on PyPI.

CodeNameMessageFix
EM101raw-string-in-exceptionException must not use a string literal, assign to variable first
EM102f-string-in-exceptionException must not use an f-string literal, assign to variable first
EM103dot-format-in-exceptionException must not use a .format() string directly, assign to variable first

flake8-implicit-str-concat (ISC)

For more, see flake8-implicit-str-concat on PyPI.

CodeNameMessageFix
ISC001single-line-implicit-string-concatenationImplicitly concatenated string literals on one line
ISC002multi-line-implicit-string-concatenationImplicitly concatenated string literals over continuation line
ISC003explicit-string-concatenationExplicitly concatenated string should be implicitly concatenated

flake8-import-conventions (ICN)

For more, see flake8-import-conventions on GitHub.

CodeNameMessageFix
ICN001import-alias-is-not-conventional{name} should be imported as {asname}

flake8-print (T20)

For more, see flake8-print on PyPI.

CodeNameMessageFix
T201print-foundprint foundπŸ› 
T203p-print-foundpprint foundπŸ› 

flake8-pytest-style (PT)

For more, see flake8-pytest-style on PyPI.

CodeNameMessageFix
PT001incorrect-fixture-parentheses-styleUse @pytest.fixture{expected_parens} over @pytest.fixture{actual_parens}πŸ› 
PT002fixture-positional-argsConfiguration for fixture {function} specified via positional args, use kwargs
PT003extraneous-scope-functionscope='function' is implied in @pytest.fixture()
PT004missing-fixture-name-underscoreFixture {function} does not return anything, add leading underscore
PT005incorrect-fixture-name-underscoreFixture {function} returns a value, remove leading underscore
PT006parametrize-names-wrong-typeWrong name(s) type in @pytest.mark.parametrize, expected {expected}πŸ› 
PT007parametrize-values-wrong-typeWrong values type in @pytest.mark.parametrize expected {values} of {row}
PT008patch-with-lambdaUse return_value= instead of patching with lambda
PT009unittest-assertionUse a regular assert instead of unittest-style {assertion}πŸ› 
PT010raises-without-exceptionset the expected exception in pytest.raises()
PT011raises-too-broadpytest.raises({exception}) is too broad, set the match parameter or use a more specific exception
PT012raises-with-multiple-statementspytest.raises() block should contain a single simple statement
PT013incorrect-pytest-importFound incorrect import of pytest, use simple import pytest instead
PT015assert-always-falseAssertion always fails, replace with pytest.fail()
PT016fail-without-messageNo message passed to pytest.fail()
PT017assert-in-exceptFound assertion on exception {name} in except block, use pytest.raises() instead
PT018composite-assertionAssertion should be broken down into multiple parts
PT019fixture-param-without-valueFixture {name} without value is injected as parameter, use @pytest.mark.usefixtures instead
PT020deprecated-yield-fixture@pytest.yield_fixture is deprecated, use @pytest.fixture
PT021fixture-finalizer-callbackUse yield instead of request.addfinalizer
PT022useless-yield-fixtureNo teardown in fixture {name}, use return instead of yieldπŸ› 
PT023incorrect-mark-parentheses-styleUse @pytest.mark.{mark_name}{expected_parens} over @pytest.mark.{mark_name}{actual_parens}πŸ› 
PT024unnecessary-asyncio-mark-on-fixturepytest.mark.asyncio is unnecessary for fixturesπŸ› 
PT025erroneous-use-fixtures-on-fixturepytest.mark.usefixtures has no effect on fixturesπŸ› 
PT026use-fixtures-without-parametersUseless pytest.mark.usefixtures without parametersπŸ› 

flake8-quotes (Q)

For more, see flake8-quotes on PyPI.

CodeNameMessageFix
Q000bad-quotes-inline-stringDouble quotes found but single quotes preferredπŸ› 
Q001bad-quotes-multiline-stringDouble quote multiline found but single quotes preferredπŸ› 
Q002bad-quotes-docstringDouble quote docstring found but single quotes preferredπŸ› 
Q003avoid-quote-escapeChange outer quotes to avoid escaping inner quotesπŸ› 

flake8-return (RET)

For more, see flake8-return on PyPI.

CodeNameMessageFix
RET501unnecessary-return-noneDo not explicitly return None in function if it is the only possible return valueπŸ› 
RET502implicit-return-valueDo not implicitly return None in function able to return non-None valueπŸ› 
RET503implicit-returnMissing explicit return at the end of function able to return non-None valueπŸ› 
RET504unnecessary-assignUnnecessary variable assignment before return statement
RET505superfluous-else-returnUnnecessary {branch} after return statement
RET506superfluous-else-raiseUnnecessary {branch} after raise statement
RET507superfluous-else-continueUnnecessary {branch} after continue statement
RET508superfluous-else-breakUnnecessary {branch} after break statement

flake8-simplify (SIM)

For more, see flake8-simplify on PyPI.

CodeNameMessageFix
SIM101duplicate-isinstance-callMultiple isinstance calls for {name}, merge into a single callπŸ› 
SIM102nested-if-statementsUse a single if statement instead of nested if statementsπŸ› 
SIM103return-bool-condition-directlyReturn the condition {cond} directlyπŸ› 
SIM105use-contextlib-suppressUse contextlib.suppress({exception}) instead of try-except-pass
SIM107return-in-try-except-finallyDon't use return in try/except and finally
SIM108use-ternary-operatorUse ternary operator {contents} instead of if-else-blockπŸ› 
SIM109compare-with-tupleUse {replacement} instead of multiple equality comparisonsπŸ› 
SIM110convert-loop-to-anyUse {any} instead of for loopπŸ› 
SIM111convert-loop-to-allUse {all} instead of for loopπŸ› 
SIM112use-capital-environment-variablesUse capitalized environment variable {expected} instead of {original}πŸ› 
SIM115open-file-with-context-handlerUse context handler for opening files
SIM117multiple-with-statementsUse a single with statement with multiple contexts instead of nested with statementsπŸ› 
SIM118key-in-dictUse {key} in {dict} instead of {key} in {dict}.keys()πŸ› 
SIM201negate-equal-opUse {left} != {right} instead of not {left} == {right}πŸ› 
SIM202negate-not-equal-opUse {left} == {right} instead of not {left} != {right}πŸ› 
SIM208double-negationUse {expr} instead of not (not {expr})πŸ› 
SIM210if-expr-with-true-falseUse bool({expr}) instead of True if {expr} else FalseπŸ› 
SIM211if-expr-with-false-trueUse not {expr} instead of False if {expr} else TrueπŸ› 
SIM212if-expr-with-twisted-armsUse {expr_else} if {expr_else} else {expr_body} instead of {expr_body} if not {expr_else} else {expr_else}πŸ› 
SIM220a-and-not-aUse False instead of {name} and not {name}πŸ› 
SIM221a-or-not-aUse True instead of {name} or not {name}πŸ› 
SIM222or-trueUse True instead of ... or TrueπŸ› 
SIM223and-falseUse False instead of ... and FalseπŸ› 
SIM300yoda-conditionsYoda conditions are discouraged, use {suggestion} insteadπŸ› 
SIM401dict-get-with-defaultUse {contents} instead of an if blockπŸ› 

flake8-tidy-imports (TID)

For more, see flake8-tidy-imports on PyPI.

CodeNameMessageFix
TID251banned-api{name} is banned: {message}
TID252relative-importsRelative imports from parent modules are banned

flake8-unused-arguments (ARG)

For more, see flake8-unused-arguments on PyPI.

CodeNameMessageFix
ARG001unused-function-argumentUnused function argument: {name}
ARG002unused-method-argumentUnused method argument: {name}
ARG003unused-class-method-argumentUnused class method argument: {name}
ARG004unused-static-method-argumentUnused static method argument: {name}
ARG005unused-lambda-argumentUnused lambda argument: {name}

flake8-datetimez (DTZ)

For more, see flake8-datetimez on PyPI.

CodeNameMessageFix
DTZ001call-datetime-without-tzinfoThe use of datetime.datetime() without tzinfo argument is not allowed
DTZ002call-datetime-todayThe use of datetime.datetime.today() is not allowed
DTZ003call-datetime-utcnowThe use of datetime.datetime.utcnow() is not allowed
DTZ004call-datetime-utcfromtimestampThe use of datetime.datetime.utcfromtimestamp() is not allowed
DTZ005call-datetime-now-without-tzinfoThe use of datetime.datetime.now() without tz argument is not allowed
DTZ006call-datetime-fromtimestampThe use of datetime.datetime.fromtimestamp() without tz argument is not allowed
DTZ007call-datetime-strptime-without-zoneThe use of datetime.datetime.strptime() without %z must be followed by .replace(tzinfo=) or .astimezone()
DTZ011call-date-todayThe use of datetime.date.today() is not allowed.
DTZ012call-date-fromtimestampThe use of datetime.date.fromtimestamp() is not allowed

eradicate (ERA)

For more, see eradicate on PyPI.

CodeNameMessageFix
ERA001commented-out-codeFound commented-out codeπŸ› 

pandas-vet (PD)

For more, see pandas-vet on PyPI.

CodeNameMessageFix
PD002use-of-inplace-argumentinplace=True should be avoided; it has inconsistent behavior
PD003use-of-dot-is-null.isna is preferred to .isnull; functionality is equivalent
PD004use-of-dot-not-null.notna is preferred to .notnull; functionality is equivalent
PD007use-of-dot-ix.ix is deprecated; use more explicit .loc or .iloc
PD008use-of-dot-atUse .loc instead of .at. If speed is important, use numpy.
PD009use-of-dot-iatUse .iloc instead of .iat. If speed is important, use numpy.
PD010use-of-dot-pivot-or-unstack.pivot_table is preferred to .pivot or .unstack; provides same functionality
PD011use-of-dot-valuesUse .to_numpy() instead of .values
PD012use-of-dot-read-table.read_csv is preferred to .read_table; provides same functionality
PD013use-of-dot-stack.melt is preferred to .stack; provides same functionality
PD015use-of-pd-mergeUse .merge method instead of pd.merge function. They have equivalent functionality.
PD901df-is-a-bad-variable-namedf is a bad variable name. Be kinder to your future self.

pygrep-hooks (PGH)

For more, see pygrep-hooks on GitHub.

CodeNameMessageFix
PGH001no-evalNo builtin eval() allowed
PGH002deprecated-log-warnwarn is deprecated in favor of warning
PGH003blanket-type-ignoreUse specific rule codes when ignoring type issues
PGH004blanket-noqaUse specific rule codes when using noqa

Pylint (PL)

For more, see Pylint on PyPI.

Convention (PLC)

CodeNameMessageFix
PLC0414useless-import-aliasImport alias does not rename original packageπŸ› 
PLC3002unnecessary-direct-lambda-callLambda expression called directly. Execute the expression inline instead.

Error (PLE)

CodeNameMessageFix
PLE0117nonlocal-without-bindingNonlocal name {name} found without binding
PLE0118used-prior-global-declarationName {name} is used prior to global declaration on line {line}
PLE1142await-outside-asyncawait should be used within an async function

Refactor (PLR)

CodeNameMessageFix
PLR0133constant-comparisonTwo constants compared in a comparison, consider replacing {left_constant} {op} {right_constant}
PLR0206property-with-parametersCannot have defined parameters for properties
PLR0402consider-using-from-importUse from {module} import {name} in lieu of alias
PLR1701consider-merging-isinstanceMerge these isinstance calls: isinstance({obj}, ({types}))
PLR1722use-sys-exitUse sys.exit() instead of {name}πŸ› 
PLR2004magic-value-comparisonMagic value used in comparison, consider replacing {value} with a constant variable

Warning (PLW)

CodeNameMessageFix
PLW0120useless-else-on-loopElse clause on loop without a break statement, remove the else and de-indent all the code inside it
PLW0602global-variable-not-assignedUsing global for {name} but no assignment is done

flake8-pie (PIE)

For more, see flake8-pie on PyPI.

CodeNameMessageFix
PIE790no-unnecessary-passUnnecessary pass statementπŸ› 
PIE794dupe-class-field-definitionsClass field {name} is defined multiple timesπŸ› 
PIE796prefer-unique-enumsEnum contains duplicate value: {value}
PIE800no-unnecessary-spreadUnnecessary spread **
PIE804no-unnecessary-dict-kwargsUnnecessary dict kwargs
PIE807prefer-list-builtinPrefer list over useless lambdaπŸ› 

flake8-commas (COM)

For more, see flake8-commas on PyPI.

CodeNameMessageFix
COM812trailing-comma-missingTrailing comma missingπŸ› 
COM818trailing-comma-on-bare-tuple-prohibitedTrailing comma on bare tuple prohibited
COM819trailing-comma-prohibitedTrailing comma prohibitedπŸ› 

flake8-no-pep420 (INP)

For more, see flake8-no-pep420 on PyPI.

CodeNameMessageFix
INP001implicit-namespace-packageFile {filename} is part of an implicit namespace package. Add an __init__.py.

flake8-executable (EXE)

For more, see flake8-executable on PyPI.

CodeNameMessageFix
EXE003shebang-pythonShebang should contain “python”
EXE004shebang-whitespaceAvoid whitespace before shebangπŸ› 
EXE005shebang-newlineShebang should be at the beginning of the file

flake8-type-checking (TYP)

For more, see flake8-type-checking on PyPI.

CodeNameMessageFix
TYP005empty-type-checking-blockFound empty type-checking block

tryceratops (TRY)

For more, see tryceratops on PyPI.

CodeNameMessageFix
TRY004prefer-type-errorPrefer TypeError exception for invalid typeπŸ› 
TRY201verbose-raiseUse raise without specifying exception name
TRY300try-consider-elseConsider else block

flake8-use-pathlib (PTH)

For more, see flake8-use-pathlib on PyPI.

CodeNameMessageFix
PTH100pathlib-abspathos.path.abspath should be replaced by .resolve()
PTH101pathlib-chmodos.chmod should be replaced by .chmod()
PTH102pathlib-mkdiros.mkdir should be replaced by .mkdir()
PTH103pathlib-makedirsos.makedirs should be replaced by .mkdir(parents=True)
PTH104pathlib-renameos.rename should be replaced by .rename()
PTH105pathlib-replaceos.replaceshould be replaced by .replace()
PTH106pathlib-rmdiros.rmdir should be replaced by .rmdir()
PTH107pathlib-removeos.remove should be replaced by .unlink()
PTH108pathlib-unlinkos.unlink should be replaced by .unlink()
PTH109pathlib-getcwdos.getcwd() should be replaced by Path.cwd()
PTH110pathlib-existsos.path.exists should be replaced by .exists()
PTH111pathlib-expanduseros.path.expanduser should be replaced by .expanduser()
PTH112pathlib-is-diros.path.isdir should be replaced by .is_dir()
PTH113pathlib-is-fileos.path.isfile should be replaced by .is_file()
PTH114pathlib-is-linkos.path.islink should be replaced by .is_symlink()
PTH115pathlib-readlinkos.readlink( should be replaced by .readlink()
PTH116pathlib-statos.stat should be replaced by .stat() or .owner() or .group()
PTH117pathlib-is-absos.path.isabs should be replaced by .is_absolute()
PTH118pathlib-joinos.path.join should be replaced by foo_path / “bar”
PTH119pathlib-basenameos.path.basename should be replaced by .name
PTH120pathlib-dirnameos.path.dirname should be replaced by .parent
PTH121pathlib-samefileos.path.samefile should be replaced by .samefile()
PTH122pathlib-splitextos.path.splitext should be replaced by .suffix
PTH123pathlib-openopen("foo") should be replaced byPath("foo").open()
PTH124pathlib-py-pathpy.path is in maintenance mode, use pathlib instead

Ruff-specific rules (RUF)

CodeNameMessageFix
RUF001ambiguous-unicode-character-stringString contains ambiguous unicode character ‘{confusable}’ (did you mean ‘{representant}’?)πŸ› 
RUF002ambiguous-unicode-character-docstringDocstring contains ambiguous unicode character ‘{confusable}’ (did you mean ‘{representant}’?)πŸ› 
RUF003ambiguous-unicode-character-commentComment contains ambiguous unicode character ‘{confusable}’ (did you mean ‘{representant}’?)πŸ› 
RUF004keyword-argument-before-star-argumentKeyword argument {name} must come after starred arguments
RUF005unpack-instead-of-concatenating-to-collection-literalConsider {expr} instead of concatenation
RUF100unused-noqaUnused blanket noqa directiveπŸ› 

Editor Integrations

VS Code (Official)

Download the Ruff VS Code extension, which supports autofix actions, import sorting, and more.

Language Server Protocol (Official)

Ruff supports the Language Server Protocol via the ruff-lsp Python package, available on PyPI.

ruff-lsp enables Ruff to be used with any editor that supports the Language Server Protocol, including Neovim, Sublime Text, Emacs, and more.

For example, to use ruff-lsp with Neovim, install ruff-lsp from PyPI along with nvim-lspconfig. Then, add something like the following to your init.lua:

-- See: https://github.com/neovim/nvim-lspconfig/tree/54eb2a070a4f389b1be0f98070f81d23e2b1a715#suggested-configuration
local opts = { noremap=true, silent=true }
vim.keymap.set('n', '<space>e', vim.diagnostic.open_float, opts)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist, opts)

-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
  -- Enable completion triggered by <c-x><c-o>
  vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')

  -- Mappings.
  -- See `:help vim.lsp.*` for documentation on any of the below functions
  local bufopts = { noremap=true, silent=true, buffer=bufnr }
  vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
  vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
  vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
  vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, bufopts)
  vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
  vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, bufopts)
  vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, bufopts)
  vim.keymap.set('n', '<space>wl', function()
    print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
  end, bufopts)
  vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, bufopts)
  vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, bufopts)
  vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, bufopts)
  vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
  vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, bufopts)
end

-- Configure `ruff-lsp`.
local configs = require 'lspconfig.configs'
if not configs.ruff_lsp then
  configs.ruff_lsp = {
    default_config = {
      cmd = { 'ruff-lsp' },
      filetypes = { 'python' },
      root_dir = require('lspconfig').util.find_git_ancestor,
      init_options = {
        settings = {
          args = {}
        }
      }
    }
  }
end
require('lspconfig').ruff_lsp.setup {
  on_attach = on_attach,
}

Upon successful installation, you should see Ruff's diagnostics surfaced directly in your editor:

Code Actions available in Neovim

To use ruff-lsp with other editors, including Sublime Text and Helix, see the ruff-lsp documentation.

Language Server Protocol (Unofficial)

Ruff is also available as the python-lsp-ruff plugin for python-lsp-server, both of which are installable from PyPI:

pip install python-lsp-server python-lsp-ruff

The LSP server can then be used with any editor that supports the Language Server Protocol.

For example, to use python-lsp-ruff with Neovim, add something like the following to your init.lua:

require'lspconfig'.pylsp.setup {
  settings = {
    pylsp = {
      plugins = {
        ruff = {
          enabled = true
        },
        pycodestyle = {
          enabled = false
        },
        pyflakes = {
          enabled = false
        },
        mccabe = {
          enabled = false
        }
      }
    }
  },
}

Vim & Neovim

Ruff can be integrated into any editor that supports the Language Server Protocol via ruff-lsp (see: Language Server Protocol), including Vim and Neovim.

It's recommended that you use ruff-lsp, the officially supported LSP server for Ruff.

However, Ruff is also available as part of the coc-pyright extension for coc.nvim.

let g:ale_linters = { "python": ["ruff"] }
let g:ale_fixers = {
\       "python": ["black", "ruff"],
\}
tools:
  python-ruff: &python-ruff
    lint-command: 'ruff --config ~/myconfigs/linters/ruff.toml --quiet ${INPUT}'
    lint-stdin: true
    lint-formats:
      - '%f:%l:%c: %m'
    format-command: 'ruff --stdin-filename ${INPUT} --config ~/myconfigs/linters/ruff.toml --fix --exit-zero --quiet -'
    format-stdin: true
local null_ls = require("null-ls")
local methods = require("null-ls.methods")
local helpers = require("null-ls.helpers")

local function ruff_fix()
    return helpers.make_builtin({
        name = "ruff",
        meta = {
            url = "https://github.com/charliermarsh/ruff/",
            description = "An extremely fast Python linter, written in Rust.",
        },
        method = methods.internal.FORMATTING,
        filetypes = { "python" },
        generator_opts = {
            command = "ruff",
            args = { "--fix", "-e", "-n", "--stdin-filename", "$FILENAME", "-" },
            to_stdin = true
        },
        factory = helpers.formatter_factory
    })
end

null_ls.setup({
    sources = {
        ruff_fix(),
        null_ls.builtins.diagnostics.ruff,
    }
})

PyCharm (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:

Install Ruff as an External Tool

Ruff should then appear as a runnable action:

Ruff as a runnable action

PyCharm (Unofficial)

Ruff is also available as the Ruff plugin on the IntelliJ Marketplace (maintained by @koxudaxi).

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@v3
      - name: Install Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.11"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install ruff
      # Include `--format=github` to enable automatic inline annotations.
      - name: Run Ruff
        run: ruff --format=github .

FAQ

Is Ruff compatible with Black?

Yes. Ruff is compatible with Black out-of-the-box, as long as the line-length setting is consistent between the two.

As a project, Ruff is designed to be used alongside Black and, as such, will defer implementing stylistic lint rules that are obviated by autoformatting.

How does Ruff compare to Flake8?

(Coming from Flake8? Try flake8-to-ruff to automatically convert your existing configuration.)

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.

Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools natively, including:

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 suffers from the following limitations vis-Γ -vis Flake8:

  1. Ruff does not yet support structural pattern matching.
  2. Flake8 has a plugin architecture and supports writing custom lint rules. (Instead, popular Flake8 plugins are re-implemented in Rust as part of Ruff itself.)

There are a few other minor incompatibilities between Ruff and the originating Flake8 plugins:

  • Ruff doesn't implement all the “opinionated” lint rules from flake8-bugbear.
  • Depending on your project structure, Ruff and isort can differ in their detection of first-party code. (This is often solved by modifying the src property, e.g., to src = ["src"], if your code is nested in a src directory.)

How does Ruff compare to Pylint?

At time of writing, Pylint implements 409 total rules, while Ruff implements 224, of which at least 60 overlap with the Pylint rule set. Subjectively, Pylint tends to implement more rules based on type inference (e.g., validating the number of arguments in a function call).

Like Flake8, Pylint supports plugins (called “checkers”), while Ruff implements all rules natively.

Unlike Pylint, Ruff is capable of automatically fixing its own lint violations.

Pylint parity is being tracked in #970.

Which tools does Ruff replace?

Today, Ruff can be used to replace Flake8 when used with any of the following plugins:

Ruff can also replace isort, yesqa, eradicate, pygrep-hooks (#980), and a subset of the rules implemented in pyupgrade (#827).

If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an Issue.

Do I need to install Rust to use Ruff?

Nope! Ruff is available as ruff on PyPI:

pip install ruff

Ruff ships with wheels for all major platforms, which enables pip to install Ruff without relying on Rust at all.

Can I write my own plugins for Ruff?

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?

Ruff's import sorting is intended to be nearly equivalent to isort when used profile = "black". There are a few known, minor differences in how Ruff and isort break ties between similar imports, and in how Ruff and isort treat inline comments in some cases (see: #1381, #2104).

Like isort, Ruff's import sorting is compatible with Black.

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. For example, you can set known-first-party like so:

[tool.ruff]
select = [
    # Pyflakes
    "F",
    # Pycodestyle
    "E",
    "W",
    # isort
    "I001"
]
src = ["src", "tests"]

[tool.ruff.isort]
known-first-party = ["my_module1", "my_module2"]

Does Ruff support Jupyter Notebooks?

Ruff is integrated into 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 error(s).
1 potentially fixable with the --fix option.

Does Ruff support NumPy- or Google-style docstrings?

Yes! To enable specific docstring convention, add the following to your pyproject.toml:

[tool.ruff.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, like so:

[tool.ruff]
select = [
    "D",
]

[tool.ruff.pydocstyle]
convention = "google"

Setting a convention force-disables any rules that are incompatible with that convention, no matter how they're provided, which avoids accidental incompatibilities and simplifies configuration.

How can I tell what settings Ruff is using to check my code?

Run ruff /path/to/code.py --show-settings to view the resolved settings for a given file.

Contributing

Contributions are welcome and hugely appreciated. To get started, check out the contributing guidelines.

Releases

Ruff is distributed on PyPI, and published via maturin.

See: .github/workflows/release.yaml.

Benchmarks

First, clone CPython. It's a large and diverse Python codebase, which makes it a good target for benchmarking.

git clone --branch 3.10 https://github.com/python/cpython.git resources/test/cpython

To benchmark the release build:

cargo build --release && hyperfine --ignore-failure --warmup 10 \
  "./target/release/ruff ./resources/test/cpython/ --no-cache" \
  "./target/release/ruff ./resources/test/cpython/"

Benchmark 1: ./target/release/ruff ./resources/test/cpython/ --no-cache
  Time (mean Β± Οƒ):     293.8 ms Β±   3.2 ms    [User: 2384.6 ms, System: 90.3 ms]
  Range (min … max):   289.9 ms … 301.6 ms    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: ./target/release/ruff ./resources/test/cpython/
  Time (mean Β± Οƒ):      48.0 ms Β±   3.1 ms    [User: 65.2 ms, System: 124.7 ms]
  Range (min … max):    45.0 ms …  66.7 ms    62 runs

  Warning: Ignoring non-zero exit code.

Summary
  './target/release/ruff ./resources/test/cpython/' ran
    6.12 Β± 0.41 times faster than './target/release/ruff ./resources/test/cpython/ --no-cache'

To benchmark against the ecosystem's existing tools:

hyperfine --ignore-failure --warmup 5 \
  "./target/release/ruff ./resources/test/cpython/ --no-cache" \
  "pyflakes resources/test/cpython" \
  "autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
  "pycodestyle resources/test/cpython" \
  "flake8 resources/test/cpython"

Benchmark 1: ./target/release/ruff ./resources/test/cpython/ --no-cache
  Time (mean Β± Οƒ):     294.3 ms Β±   3.3 ms    [User: 2467.5 ms, System: 89.6 ms]
  Range (min … max):   291.1 ms … 302.8 ms    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: pyflakes resources/test/cpython
  Time (mean Β± Οƒ):     15.786 s Β±  0.143 s    [User: 15.560 s, System: 0.214 s]
  Range (min … max):   15.640 s … 16.157 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 3: autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython
  Time (mean Β± Οƒ):      6.175 s Β±  0.169 s    [User: 54.102 s, System: 1.057 s]
  Range (min … max):    5.950 s …  6.391 s    10 runs

Benchmark 4: pycodestyle resources/test/cpython
  Time (mean Β± Οƒ):     46.921 s Β±  0.508 s    [User: 46.699 s, System: 0.202 s]
  Range (min … max):   46.171 s … 47.863 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 5: flake8 resources/test/cpython
  Time (mean Β± Οƒ):     12.260 s Β±  0.321 s    [User: 102.934 s, System: 1.230 s]
  Range (min … max):   11.848 s … 12.933 s    10 runs

  Warning: Ignoring non-zero exit code.

Summary
  './target/release/ruff ./resources/test/cpython/ --no-cache' ran
   20.98 Β± 0.62 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
   41.66 Β± 1.18 times faster than 'flake8 resources/test/cpython'
   53.64 Β± 0.77 times faster than 'pyflakes resources/test/cpython'
  159.43 Β± 2.48 times faster than 'pycodestyle resources/test/cpython'

You can run poetry install from ./scripts to create a working environment for the above. All reported benchmarks were computed using the versions specified by ./scripts/pyproject.toml on Python 3.11.

To benchmark Pylint, remove the following files from the CPython repository:

rm Lib/test/bad_coding.py \
  Lib/test/bad_coding2.py \
  Lib/test/bad_getattr.py \
  Lib/test/bad_getattr2.py \
  Lib/test/bad_getattr3.py \
  Lib/test/badcert.pem \
  Lib/test/badkey.pem \
  Lib/test/badsyntax_3131.py \
  Lib/test/badsyntax_future10.py \
  Lib/test/badsyntax_future3.py \
  Lib/test/badsyntax_future4.py \
  Lib/test/badsyntax_future5.py \
  Lib/test/badsyntax_future6.py \
  Lib/test/badsyntax_future7.py \
  Lib/test/badsyntax_future8.py \
  Lib/test/badsyntax_future9.py \
  Lib/test/badsyntax_pep3120.py \
  Lib/test/test_asyncio/test_runners.py \
  Lib/test/test_copy.py \
  Lib/test/test_inspect.py \
  Lib/test/test_typing.py

Then, from resources/test/cpython, run: time pylint -j 0 -E $(git ls-files '*.py'). This will execute Pylint with maximum parallelism and only report errors.

To benchmark Pyupgrade, run the following from resources/test/cpython:

hyperfine --ignore-failure --warmup 5 --prepare "git reset --hard HEAD" \
  "find . -type f -name \"*.py\" | xargs -P 0 pyupgrade --py311-plus"

Benchmark 1: find . -type f -name "*.py" | xargs -P 0 pyupgrade --py311-plus
  Time (mean Β± Οƒ):     30.119 s Β±  0.195 s    [User: 28.638 s, System: 0.390 s]
  Range (min … max):   29.813 s … 30.356 s    10 runs

Reference

Options

allowed-confusables

A list of allowed “confusable” Unicode characters to ignore when enforcing RUF001, RUF002, and RUF003.

Default value: []

Type: Vec<char>

Example usage:

[tool.ruff]
# Allow minus-sign (U+2212), greek-small-letter-rho (U+03C1), and the asterisk-operator (U+2217),
# which could be confused for "-", "p", and "*", respectively.
allowed-confusables = ["βˆ’", "ρ", "βˆ—"]

builtins

A list of builtins to treat as defined references, in addition to the system builtins.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff]
builtins = ["_"]

cache-dir

A path to the cache directory.

By default, Ruff stores cache results in a .ruff_cache directory in the current project root.

However, Ruff will also respect the RUFF_CACHE_DIR environment variable, which takes precedence over that default.

This setting will override even the RUFF_CACHE_DIR environment variable, if set.

Default value: .ruff_cache

Type: PathBuf

Example usage:

[tool.ruff]
cache-dir = "~/.cache/ruff"

dummy-variable-rgx

A regular expression used to identify “dummy” variables, or those which should be ignored when enforcing (e.g.) unused-variable rules. The default expression matches _, __, and _var, but not _var_.

Default value: "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

Type: Regex

Example usage:

[tool.ruff]
# Only ignore variables named "_".
dummy-variable-rgx = "^_$"

exclude

A list of file patterns to exclude from linting.

Exclusions are based on globs, and can be either:

  • Single-path patterns, like .mypy_cache (to exclude any directory named .mypy_cache in the tree), foo.py (to exclude any file named foo.py), or foo_*.py (to exclude any file matching foo_*.py ).
  • Relative patterns, like directory/foo.py (to exclude that specific file) or directory/*.py (to exclude any Python files in directory). Note that these paths are relative to the project root (e.g., the directory containing your pyproject.toml).

For more information on the glob syntax, refer to the globset documentation.

Note that you'll typically want to use extend-exclude to modify the excluded paths.

Default value: [".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]

Type: Vec<FilePattern>

Example usage:

[tool.ruff]
exclude = [".venv"]

extend

A path to a local pyproject.toml file to merge into this configuration. User home directory and environment variables will be expanded.

To resolve the current pyproject.toml file, Ruff will first resolve this base configuration file, then merge in any properties defined in the current configuration file.

Default value: None

Type: Path

Example usage:

[tool.ruff]
# Extend the `pyproject.toml` file in the parent directory.
extend = "../pyproject.toml"
# But use a different line length.
line-length = 100

extend-exclude

A list of file patterns to omit from linting, in addition to those specified by exclude.

Exclusions are based on globs, and can be either:

  • Single-path patterns, like .mypy_cache (to exclude any directory named .mypy_cache in the tree), foo.py (to exclude any file named foo.py), or foo_*.py (to exclude any file matching foo_*.py ).
  • Relative patterns, like directory/foo.py (to exclude that specific file) or directory/*.py (to exclude any Python files in directory). Note that these paths are relative to the project root (e.g., the directory containing your pyproject.toml).

For more information on the glob syntax, refer to the globset documentation.

Default value: []

Type: Vec<FilePattern>

Example usage:

[tool.ruff]
# In addition to the standard set of exclusions, omit all tests, plus a specific file.
extend-exclude = ["tests", "src/bad.py"]

extend-ignore

A list of rule codes or prefixes to ignore, in addition to those specified by ignore.

Note that extend-ignore is applied after resolving rules from ignore/select and a less specific rule in extend-ignore would overwrite a more specific rule in select. It is recommended to only use extend-ignore when extending a pyproject.toml file via extend.

Default value: []

Type: Vec<RuleSelector>

Example usage:

[tool.ruff]
# Skip unused variable rules (`F841`).
extend-ignore = ["F841"]

extend-select

A list of rule codes or prefixes to enable, in addition to those specified by select.

Note that extend-select is applied after resolving rules from ignore/select and a less specific rule in extend-select would overwrite a more specific rule in ignore. It is recommended to only use extend-select when extending a pyproject.toml file via extend.

Default value: []

Type: Vec<RuleSelector>

Example usage:

[tool.ruff]
# On top of the default `select` (`E`, `F`), enable flake8-bugbear (`B`) and flake8-quotes (`Q`).
extend-select = ["B", "Q"]

external

A list of rule codes that are unsupported by Ruff, but should be preserved when (e.g.) validating # noqa directives. Useful for retaining # noqa directives that cover plugins not yet implemented by Ruff.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff]
# Avoiding flagging (and removing) `V101` from any `# noqa`
# directives, despite Ruff's lack of support for `vulture`.
external = ["V101"]

fix

Enable autofix behavior by-default when running ruff (overridden by the --fix and --no-fix command-line flags).

Default value: false

Type: bool

Example usage:

[tool.ruff]
fix = true

fix-only

Like fix, but disables reporting on leftover violation. Implies fix.

Default value: false

Type: bool

Example usage:

[tool.ruff]
fix-only = true

fixable

A list of rule codes or prefixes to consider autofixable.

Default value: ["A", "ANN", "ARG", "B", "BLE", "C", "D", "E", "ERA", "F", "FBT", "I", "ICN", "N", "PGH", "PLC", "PLE", "PLR", "PLW", "Q", "RET", "RUF", "S", "T", "TID", "UP", "W", "YTT"]

Type: Vec<RuleSelector>

Example usage:

[tool.ruff]
# Only allow autofix behavior for `E` and `F` rules.
fixable = ["E", "F"]

force-exclude

Whether to enforce exclude and extend-exclude patterns, even for paths that are passed to Ruff explicitly. Typically, Ruff will lint any paths passed in directly, even if they would typically be excluded. Setting force-exclude = true will cause Ruff to respect these exclusions unequivocally.

This is useful for pre-commit, which explicitly passes all changed files to the ruff-pre-commit plugin, regardless of whether they‘re marked as excluded by Ruff’s own settings.

Default value: false

Type: bool

Example usage:

[tool.ruff]
force-exclude = true

format

The style in which violation messages should be formatted: "text" (default), "grouped" (group messages by file), "json" (machine-readable), "junit" (machine-readable XML), "github" (GitHub Actions annotations), "gitlab" (GitLab CI code quality report), or "pylint" (Pylint text format).

Default value: "text"

Type: SerializationType

Example usage:

[tool.ruff]
# Group violations by containing file.
format = "grouped"

ignore

A list of rule codes or prefixes to ignore. Prefixes can specify exact rules (like F841), entire categories (like F), or anything in between.

When breaking ties between enabled and disabled rules (via select and ignore, respectively), more specific prefixes override less specific prefixes.

Default value: []

Type: Vec<RuleSelector>

Example usage:

[tool.ruff]
# Skip unused variable rules (`F841`).
ignore = ["F841"]

ignore-init-module-imports

Avoid automatically removing unused imports in __init__.py files. Such imports will still be flagged, but with a dedicated message suggesting that the import is either added to the module's __all__ symbol, or re-exported with a redundant alias (e.g., import os as os).

Default value: false

Type: bool

Example usage:

[tool.ruff]
ignore-init-module-imports = true

line-length

The line length to use when enforcing long-lines violations (like E501).

Default value: 88

Type: usize

Example usage:

[tool.ruff]
# Allow lines to be as long as 120 characters.
line-length = 120

namespace-packages

Mark the specified directories as namespace packages. For the purpose of module resolution, Ruff will treat those directories as if they contained an __init__.py file.

Default value: []

Type: Vec<PathBuf>

Example usage:

[tool.ruff]
namespace-packages = ["airflow/providers"]

per-file-ignores

A list of mappings from file pattern to rule codes or prefixes to exclude, when considering any matching files.

Default value: {}

Type: HashMap<String, Vec<RuleSelector>>

Example usage:

[tool.ruff]
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
[tool.ruff.per-file-ignores]
"__init__.py" = ["E402"]
"path/to/file.py" = ["E402"]

required-version

Require a specific version of Ruff to be running (useful for unifying results across many environments, e.g., with a pyproject.toml file).

Default value: None

Type: String

Example usage:

[tool.ruff]
required-version = "0.0.193"

respect-gitignore

Whether to automatically exclude files that are ignored by .ignore, .gitignore, .git/info/exclude, and global gitignore files. Enabled by default.

Default value: true

Type: bool

Example usage:

[tool.ruff]
respect_gitignore = false

select

A list of rule codes or prefixes to enable. Prefixes can specify exact rules (like F841), entire categories (like F), or anything in between.

When breaking ties between enabled and disabled rules (via select and ignore, respectively), more specific prefixes override less specific prefixes.

Default value: ["E", "F"]

Type: Vec<RuleSelector>

Example usage:

[tool.ruff]
# On top of the defaults (`E`, `F`), enable flake8-bugbear (`B`) and flake8-quotes (`Q`).
select = ["E", "F", "B", "Q"]

show-source

Whether to show source code snippets when reporting lint violations (overridden by the --show-source command-line flag).

Default value: false

Type: bool

Example usage:

[tool.ruff]
# By default, always show source code snippets.
show-source = true

src

The source code paths to consider, e.g., when resolving first- vs. third-party imports.

As an example: given a Python package structure like:

my_package/
  pyproject.toml
  src/
    my_package/
      __init__.py
      foo.py
      bar.py

The src directory should be included in source (e.g., source = ["src"]), such that when resolving imports, my_package.foo is considered a first-party import.

This field supports globs. For example, if you have a series of Python packages in a python_modules directory, src = ["python_modules/*"] would expand to incorporate all of the packages in that directory. User home directory and environment variables will also be expanded.

Default value: ["."]

Type: Vec<PathBuf>

Example usage:

[tool.ruff]
# Allow imports relative to the "src" and "test" directories.
src = ["src", "test"]

target-version

The Python version to target, e.g., when considering automatic code upgrades, like rewriting type annotations. Note that the target version will not be inferred from the current Python version, and instead must be specified explicitly (as seen below).

Default value: "py310"

Type: PythonVersion

Example usage:

[tool.ruff]
# Always generate Python 3.7-compatible code.
target-version = "py37"

task-tags

A list of task tags to recognize (e.g., “TODO”, “FIXME”, “XXX”).

Comments starting with these tags will be ignored by commented-out code detection (ERA), and skipped by line-length rules (E501) if ignore-overlong-task-comments is set to true.

Default value: ["TODO", "FIXME", "XXX"]

Type: Vec<String>

Example usage:

[tool.ruff]
task-tags = ["HACK"]

typing-modules

A list of modules whose imports should be treated equivalently to members of the typing module.

This is useful for ensuring proper type annotation inference for projects that re-export typing and typing_extensions members from a compatibility module. If omitted, any members imported from modules apart from typing and typing_extensions will be treated as ordinary Python objects.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff]
typing-modules = ["airflow.typing_compat"]

unfixable

A list of rule codes or prefixes to consider non-autofix-able.

Default value: []

Type: Vec<RuleSelector>

Example usage:

[tool.ruff]
# Disable autofix for unused imports (`F401`).
unfixable = ["F401"]

update-check

Enable or disable automatic update checks (overridden by the --update-check and --no-update-check command-line flags).

Default value: false

Type: bool

Example usage:

[tool.ruff]
update-check = true

flake8-annotations

allow-star-arg-any

Whether to suppress ANN401 for dynamically typed *args and **kwargs arguments.

Default value: false

Type: bool

Example usage:

[tool.ruff.flake8-annotations]
allow-star-arg-any = true

mypy-init-return

Whether to allow the omission of a return type hint for __init__ if at least one argument is annotated.

Default value: false

Type: bool

Example usage:

[tool.ruff.flake8-annotations]
mypy-init-return = true

suppress-dummy-args

Whether to suppress ANN000-level violations for arguments matching the “dummy” variable regex (like _).

Default value: false

Type: bool

Example usage:

[tool.ruff.flake8-annotations]
suppress-dummy-args = true

suppress-none-returning

Whether to suppress ANN200-level violations for functions that meet either of the following criteria:

  • Contain no return statement.
  • Explicit return statement(s) all return None (explicitly or implicitly).

Default value: false

Type: bool

Example usage:

[tool.ruff.flake8-annotations]
suppress-none-returning = true

flake8-bandit

hardcoded-tmp-directory

A list of directories to consider temporary.

Default value: ["/tmp", "/var/tmp", "/dev/shm"]

Type: Vec<String>

Example usage:

[tool.ruff.flake8-bandit]
hardcoded-tmp-directory = ["/foo/bar"]

hardcoded-tmp-directory-extend

A list of directories to consider temporary, in addition to those specified by hardcoded-tmp-directory.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.flake8-bandit]
extend-hardcoded-tmp-directory = ["/foo/bar"]

flake8-bugbear

extend-immutable-calls

Additional callable functions to consider “immutable” when evaluating, e.g., the no-mutable-default-argument rule (B006).

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.flake8-bugbear]
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]

flake8-errmsg

max-string-length

Maximum string length for string literals in exception messages.

Default value: 0

Type: usize

Example usage:

[tool.ruff.flake8-errmsg]
max-string-length = 20

flake8-import-conventions

aliases

The conventional aliases for imports. These aliases can be extended by the extend_aliases option.

Default value: {"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}

Type: FxHashMap<String, String>

Example usage:

[tool.ruff.flake8-import-conventions]
[tool.ruff.flake8-import-conventions.aliases]
# Declare the default aliases.
altair = "alt"
"matplotlib.pyplot" = "plt"
numpy = "np"
pandas = "pd"
seaborn = "sns"

extend-aliases

A mapping of modules to their conventional import aliases. These aliases will be added to the aliases mapping.

Default value: {}

Type: FxHashMap<String, String>

Example usage:

[tool.ruff.flake8-import-conventions]
[tool.ruff.flake8-import-conventions.extend-aliases]
# Declare a custom alias for the `matplotlib` module.
"dask.dataframe" = "dd"

flake8-pytest-style

fixture-parentheses

Boolean flag specifying whether @pytest.fixture() without parameters should have parentheses. If the option is set to true (the default), @pytest.fixture() is valid and @pytest.fixture is invalid. If set to false, @pytest.fixture is valid and @pytest.fixture() is invalid.

Default value: true

Type: bool

Example usage:

[tool.ruff.flake8-pytest-style]
fixture-parentheses = true

mark-parentheses

Boolean flag specifying whether @pytest.mark.foo() without parameters should have parentheses. If the option is set to true (the default), @pytest.mark.foo() is valid and @pytest.mark.foo is invalid. If set to false, @pytest.fixture is valid and @pytest.mark.foo() is invalid.

Default value: true

Type: bool

Example usage:

[tool.ruff.flake8-pytest-style]
mark-parentheses = true

parametrize-names-type

Expected type for multiple argument names in @pytest.mark.parametrize. The following values are supported:

  • csv β€” a comma-separated list, e.g. @pytest.mark.parametrize('name1,name2', ...)
  • tuple (default) β€” e.g. @pytest.mark.parametrize(('name1', 'name2'), ...)
  • list β€” e.g. @pytest.mark.parametrize(['name1', 'name2'], ...)

Default value: tuple

Type: ParametrizeNameType

Example usage:

[tool.ruff.flake8-pytest-style]
parametrize-names-type = "list"

parametrize-values-row-type

Expected type for each row of values in @pytest.mark.parametrize in case of multiple parameters. The following values are supported:

  • tuple (default) β€” e.g. @pytest.mark.parametrize(('name1', 'name2'), [(1, 2), (3, 4)])
  • list β€” e.g. @pytest.mark.parametrize(('name1', 'name2'), [[1, 2], [3, 4]])

Default value: tuple

Type: ParametrizeValuesRowType

Example usage:

[tool.ruff.flake8-pytest-style]
parametrize-values-row-type = "list"

parametrize-values-type

Expected type for the list of values rows in @pytest.mark.parametrize. The following values are supported:

  • tuple β€” e.g. @pytest.mark.parametrize('name', (1, 2, 3))
  • list (default) β€” e.g. @pytest.mark.parametrize('name', [1, 2, 3])

Default value: list

Type: ParametrizeValuesType

Example usage:

[tool.ruff.flake8-pytest-style]
parametrize-values-type = "tuple"

raises-extend-require-match-for

List of additional exception names that require a match= parameter in a pytest.raises() call. This extends the default list of exceptions that require a match= parameter. This option is useful if you want to extend the default list of exceptions that require a match= parameter without having to specify the entire list. Note that this option does not remove any exceptions from the default list.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.flake8-pytest-style]
raises-extend-require-match-for = ["requests.RequestException"]

raises-require-match-for

List of exception names that require a match= parameter in a pytest.raises() call.

Default value: ["BaseException", "Exception", "ValueError", "OSError", "IOError", "EnvironmentError", "socket.error"]

Type: Vec<String>

Example usage:

[tool.ruff.flake8-pytest-style]
raises-require-match-for = ["requests.RequestException"]

flake8-quotes

avoid-escape

Whether to avoid using single quotes if a string contains single quotes, or vice-versa with double quotes, as per PEP8. This minimizes the need to escape quotation marks within strings.

Default value: true

Type: bool

Example usage:

[tool.ruff.flake8-quotes]
# Don't bother trying to avoid escapes.
avoid-escape = false

docstring-quotes

Quote style to prefer for docstrings (either “single” (') or “double” (")).

Default value: "double"

Type: Quote

Example usage:

[tool.ruff.flake8-quotes]
docstring-quotes = "single"

inline-quotes

Quote style to prefer for inline strings (either “single” (') or “double” (")).

Default value: "double"

Type: Quote

Example usage:

[tool.ruff.flake8-quotes]
inline-quotes = "single"

multiline-quotes

Quote style to prefer for multiline strings (either “single” (') or “double” (")).

Default value: "double"

Type: Quote

Example usage:

[tool.ruff.flake8-quotes]
multiline-quotes = "single"

flake8-tidy-imports

ban-relative-imports

Whether to ban all relative imports ("all"), or only those imports that extend into the parent module or beyond ("parents").

Default value: "parents"

Type: Strictness

Example usage:

[tool.ruff.flake8-tidy-imports]
# Disallow all relative imports.
ban-relative-imports = "all"

banned-api

Specific modules or module members that may not be imported or accessed. Note that this rule is only meant to flag accidental uses, and can be circumvented via eval or importlib.

Default value: {}

Type: HashMap<String, BannedApi>

Example usage:

[tool.ruff.flake8-tidy-imports]
[tool.ruff.flake8-tidy-imports.banned-api]
"cgi".msg = "The cgi module is deprecated, see https://peps.python.org/pep-0594/#cgi."
"typing.TypedDict".msg = "Use typing_extensions.TypedDict instead."

flake8-unused-arguments

ignore-variadic-names

Whether to allow unused variadic arguments, like *args and **kwargs.

Default value: false

Type: bool

Example usage:

[tool.ruff.flake8-unused-arguments]
ignore-variadic-names = true

isort

classes

An override list of tokens to always recognize as a Class for order-by-type regardless of casing.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
classes = ["SVC"]

combine-as-imports

Combines as imports on the same line. See isort's combine-as-imports option.

Default value: false

Type: bool

Example usage:

[tool.ruff.isort]
combine-as-imports = true

constants

An override list of tokens to always recognize as a CONSTANT for order-by-type regardless of casing.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
constants = ["constant"]

extra-standard-library

A list of modules to consider standard-library, in addition to those known to Ruff in advance.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
extra-standard-library = ["path"]

force-single-line

Forces all from imports to appear on their own line.

Default value: false

Type: bool

Example usage:

[tool.ruff.isort]
force-single-line = true

force-sort-within-sections

Don't sort straight-style imports (like import sys) before from-style imports (like from itertools import groupby). Instead, sort the imports by module, independent of import style.

Default value: false

Type: bool

Example usage:

[tool.ruff.isort]
force-sort-within-sections = true

force-wrap-aliases

Force import from statements with multiple members and at least one alias (e.g., import A as B) to wrap such that every line contains exactly one member. For example, this formatting would be retained, rather than condensing to a single line:

from .utils import (
    test_directory as test_directory,
    test_id as test_id
)

Note that this setting is only effective when combined with combine-as-imports = true. When combine-as-imports isn't enabled, every aliased import from will be given its own line, in which case, wrapping is not necessary.

Default value: false

Type: bool

Example usage:

[tool.ruff.isort]
force-wrap-aliases = true
combine-as-imports = true

known-first-party

A list of modules to consider first-party, regardless of whether they can be identified as such via introspection of the local filesystem.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
known-first-party = ["src"]

known-third-party

A list of modules to consider third-party, regardless of whether they can be identified as such via introspection of the local filesystem.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
known-third-party = ["src"]

no-lines-before

A list of sections that should not be delineated from the previous section via empty lines.

Default value: []

Type: Option<Vec<ImportType>>

Example usage:

[tool.ruff.isort]
no-lines-before = ["future", "standard-library"]

order-by-type

Order imports by type, which is determined by case, in addition to alphabetically.

Default value: true

Type: bool

Example usage:

[tool.ruff.isort]
order-by-type = true

relative-imports-order

Whether to place “closer” imports (fewer . characters, most local) before “further” imports (more . characters, least local), or vice versa.

The default (“furthest-to-closest”) is equivalent to isort‘s reverse-relative default (reverse-relative = false); setting this to “closest-to-furthest” is equivalent to isort’s reverse-relative = true.

Default value: furthest-to-closest

Type: RelatveImportsOrder

Example usage:

[tool.ruff.isort]
relative-imports-order = "closest-to-furthest"

required-imports

Add the specified import line to all files.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
required-imports = ["from __future__ import annotations"]

single-line-exclusions

One or more modules to exclude from the single line rule.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
single-line-exclusions = ["os", "json"]

split-on-trailing-comma

If a comma is placed after the last member in a multi-line import, then the imports will never be folded into one line.

See isort's split-on-trailing-comma option.

Default value: true

Type: bool

Example usage:

[tool.ruff.isort]
split-on-trailing-comma = false

variables

An override list of tokens to always recognize as a var for order-by-type regardless of casing.

Default value: []

Type: Vec<String>

Example usage:

[tool.ruff.isort]
variables = ["VAR"]

mccabe

max-complexity

The maximum McCabe complexity to allow before triggering C901 errors.

Default value: 10

Type: usize

Example usage:

[tool.ruff.mccabe]
# Flag errors (`C901`) whenever the complexity level exceeds 5.
max-complexity = 5

pep8-naming

classmethod-decorators

A list of decorators that, when applied to a method, indicate that the method should be treated as a class method. For example, Ruff will expect that any method decorated by a decorator in this list takes a cls argument as its first argument.

Default value: ["classmethod"]

Type: Vec<String>

Example usage:

[tool.ruff.pep8-naming]
# Allow Pydantic's `@validator` decorator to trigger class method treatment.
classmethod-decorators = ["classmethod", "pydantic.validator"]

ignore-names

A list of names to ignore when considering pep8-naming violations.

Default value: ["setUp", "tearDown", "setUpClass", "tearDownClass", "setUpModule", "tearDownModule", "asyncSetUp", "asyncTearDown", "setUpTestData", "failureException", "longMessage", "maxDiff"]

Type: Vec<String>

Example usage:

[tool.ruff.pep8-naming]
ignore-names = ["callMethod"]

staticmethod-decorators

A list of decorators that, when applied to a method, indicate that the method should be treated as a static method. For example, Ruff will expect that any method decorated by a decorator in this list has no self or cls argument.

Default value: ["staticmethod"]

Type: Vec<String>

Example usage:

[tool.ruff.pep8-naming]
# Allow a shorthand alias, `@stcmthd`, to trigger static method treatment.
staticmethod-decorators = ["staticmethod", "stcmthd"]

pycodestyle

ignore-overlong-task-comments

Whether or not line-length violations (E501) should be triggered for comments starting with task-tags (by default: [“TODO”, “FIXME”, and “XXX”]).

Default value: false

Type: bool

Example usage:

[tool.ruff.pycodestyle]
ignore-overlong-task-comments = true

max-doc-length

The maximum line length to allow for line-length violations within documentation (W505), including standalone comments.

Default value: None

Type: usize

Example usage:

[tool.ruff.pycodestyle]
max-doc-length = 88

pydocstyle

convention

Whether to use Google-style or NumPy-style conventions or the PEP257 defaults when analyzing docstring sections.

Default value: None

Type: Convention

Example usage:

[tool.ruff.pydocstyle]
# Use Google-style docstrings.
convention = "google"

pylint

allow-magic-value-types

Constant types to ignore when used as “magic values”.

Default value: ["str"]

Type: Vec<ConstantType>

Example usage:

[tool.ruff.pylint]
allow-magic-value-types = ["int"]

pyupgrade

keep-runtime-typing

Whether to avoid PEP 585 (List[int] -> list[int]) and PEP 604 (Optional[str] -> str | None) rewrites even if a file imports from __future__ import annotations. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.

Default value: false

Type: bool

Example usage:

[tool.ruff.pyupgrade]
# Preserve types, even if a file imports `from __future__ import annotations`.
keep-runtime-typing = true

License

MIT