Skip to main content

An extremely fast Python linter, written in Rust.

Project description

Ruff

image image image Actions status

An extremely fast Python linter, written in Rust.

Bar chart with benchmark results

Linting the CPython codebase from scratch.

  • โšก๏ธ 10-100x faster than existing linters
  • ๐Ÿ Installable via pip
  • ๐Ÿค Python 3.10 compatibility
  • ๐Ÿ› ๏ธ pyproject.toml support
  • ๐Ÿ“ฆ ESLint-inspired cache support
  • ๐Ÿ”ง ESLint-inspired autofix support (e.g., automatically remove unused imports)
  • ๐Ÿ‘€ TypeScript-inspired --watch support, for continuous file monitoring
  • โš–๏ธ Near-parity with the built-in Flake8 rule set
  • ๐Ÿ”Œ Native re-implementations of popular Flake8 plugins, like flake8-docstrings (pydocstyle)

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), pydocstyle, yesqa, and even a subset of pyupgrade and autoflake all while executing tens or hundreds of times faster than any individual tool.

Read the launch blog post.

Table of Contents

  1. Installation and Usage
  2. Configuration
  3. Supported Rules
    1. Pyflakes
    2. pycodestyle (error)
    3. pycodestyle (warning)
    4. pydocstyle
    5. pyupgrade
    6. pep8-naming
    7. flake8-comprehensions
    8. flake8-bugbear
    9. flake8-builtins
    10. flake8-print
    11. flake8-quotes
    12. Meta rules
  4. Editor Integrations
  5. FAQ
  6. Development
  7. Releases
  8. Benchmarks
  9. License
  10. Contributing

Installation and Usage

Installation

Available as ruff on PyPI:

pip install ruff

Usage

To run Ruff, try any of the following:

ruff path/to/code/to/check.py
ruff path/to/code/
ruff path/to/code/*.py

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

ruff path/to/code/ --watch

Ruff also works with pre-commit:

repos:
  - repo: https://github.com/charliermarsh/ruff-pre-commit
    rev: v0.0.98
    hooks:
      - id: ruff

Note: prior to v0.0.86, ruff-pre-commit used lint (rather than ruff) as the hook ID.

Configuration

Ruff is configurable both via pyproject.toml and the command line. If left unspecified, the default configuration is equivalent to:

[tool.ruff]
line-length = 88

# Enable Flake's "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"

As an example, the following would configure Ruff to (1) avoid checking for line-length violations (E501) and (2) ignore unused import rules in __init__.py files:

[tool.ruff]
select = ["E", "F"]

# Never enforce `E501`.
ignore = ["E501"]

# Ignore `F401` violations in any `__init__.py` file, and in `path/to/file.py`.
per-file-ignores = {"__init__.py" = ["F401"], "path/to/file.py" = ["F401"]}

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"

Alternatively, 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` file to use for configuration
  -v, --verbose
          Enable verbose logging
  -q, --quiet
          Only log errors
  -s, --silent
          Disable all logging (but still exit with status code "1" upon detecting errors)
  -e, --exit-zero
          Exit with status code "0", even upon detecting errors
  -w, --watch
          Run in watch mode by re-running whenever files change
  -f, --fix
          Attempt to automatically fix lint errors
  -n, --no-cache
          Disable cache reads
      --select <SELECT>
          List of error codes to enable
      --extend-select <EXTEND_SELECT>
          Like --select, but adds additional error codes on top of the selected ones
      --ignore <IGNORE>
          List of error codes to ignore
      --extend-ignore <EXTEND_IGNORE>
          Like --ignore, but adds additional error codes on top of the ignored ones
      --exclude <EXCLUDE>
          List of paths, used to exclude files and/or directories from checks
      --extend-exclude <EXTEND_EXCLUDE>
          Like --exclude, but adds additional files and directories on top of the excluded ones
      --per-file-ignores <PER_FILE_IGNORES>
          List of mappings from file pattern to code to exclude
      --format <FORMAT>
          Output serialization format for error messages [default: text] [possible values: text, json]
      --show-files
          See the files ruff will be run against with the current settings
      --show-settings
          See ruff's settings
      --add-noqa
          Enable automatic additions of noqa directives to failing lines
      --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
      --stdin-filename <STDIN_FILENAME>
          The name of the file when passing it through stdin
  -h, --help
          Print help information
  -V, --version
          Print version information

Excluding files

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

Ignoring errors

To omit a lint check entirely, add it to the "ignore" list via --ignore or --extend-ignore, either on the command-line or in your project.toml file.

To ignore an error in-line, Ruff uses a noqa system similar to Flake8. To ignore an individual error, 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_ errors.
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 body, 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

Ruff supports several workflows to aid in noqa management.

First, Ruff provides a special error code, M001, to enforce that your noqa directives are "valid", in that the errors they say they ignore are actually being triggered on that line (and thus suppressed). You can run ruff /path/to/file.py --extend-select M001 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 M001 --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 error codes.

Supported Rules

By default, Ruff enables all E and F error 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

Code Name Message Fix
F401 UnusedImport ... imported but unused ๐Ÿ› 
F402 ImportShadowedByLoopVar Import ... from line 1 shadowed by loop variable
F403 ImportStarUsed from ... import * used; unable to detect undefined names
F404 LateFutureImport from __future__ imports must occur at the beginning of the file
F405 ImportStarUsage ... may be undefined, or defined from star imports: ...
F406 ImportStarNotPermitted from ... import * only allowed at module level
F407 FutureFeatureNotDefined Future feature ... is not defined
F541 FStringMissingPlaceholders f-string without any placeholders
F601 MultiValueRepeatedKeyLiteral Dictionary key literal repeated
F602 MultiValueRepeatedKeyVariable Dictionary key ... repeated
F621 ExpressionsInStarAssignment Too many expressions in star-unpacking assignment
F622 TwoStarredExpressions Two starred expressions in assignment
F631 AssertTuple Assert test is a non-empty tuple, which is always True
F632 IsLiteral Use == and != to compare constant literals
F633 InvalidPrintSyntax Use of >> is invalid with print function
F634 IfTuple If test is a tuple, which is always True
F701 BreakOutsideLoop break outside loop
F702 ContinueOutsideLoop continue not properly in loop
F704 YieldOutsideFunction yield or yield from statement outside of a function
F706 ReturnOutsideFunction return statement outside of a function/method
F707 DefaultExceptNotLast An except: block as not the last exception handler
F722 ForwardAnnotationSyntaxError Syntax error in forward annotation: ...
F821 UndefinedName Undefined name ...
F822 UndefinedExport Undefined name ... in __all__
F823 UndefinedLocal Local variable ... referenced before assignment
F831 DuplicateArgumentName Duplicate argument name in function definition
F841 UnusedVariable Local variable ... is assigned to but never used
F901 RaiseNotImplemented raise NotImplemented should be raise NotImplementedError

pycodestyle (error)

Code Name Message Fix
E402 ModuleImportNotAtTopOfFile Module level import not at top of file
E501 LineTooLong Line too long (89 > 88 characters)
E711 NoneComparison Comparison to None should be cond is None
E712 TrueFalseComparison Comparison to True should be cond is True
E713 NotInTest Test for membership should be not in
E714 NotIsTest Test for object identity should be is not
E721 TypeComparison Do not compare types, use isinstance()
E722 DoNotUseBareExcept Do not use bare except
E731 DoNotAssignLambda Do not assign a lambda expression, use a def
E741 AmbiguousVariableName Ambiguous variable name: ...
E742 AmbiguousClassName Ambiguous class name: ...
E743 AmbiguousFunctionName Ambiguous function name: ...
E902 IOError IOError: ...
E999 SyntaxError SyntaxError: ...

pycodestyle (warning)

Code Name Message Fix
W292 NoNewLineAtEndOfFile No newline at end of file
W605 InvalidEscapeSequence Invalid escape sequence: '\c'

pydocstyle

Code Name Message Fix
D100 PublicModule Missing docstring in public module
D101 PublicClass Missing docstring in public class
D102 PublicMethod Missing docstring in public method
D103 PublicFunction Missing docstring in public function
D104 PublicPackage Missing docstring in public package
D105 MagicMethod Missing docstring in magic method
D106 PublicNestedClass Missing docstring in public nested class
D107 PublicInit Missing docstring in __init__
D200 FitsOnOneLine One-line docstring should fit on one line
D201 NoBlankLineBeforeFunction No blank lines allowed before function docstring (found 1) ๐Ÿ› 
D202 NoBlankLineAfterFunction No blank lines allowed after function docstring (found 1) ๐Ÿ› 
D203 OneBlankLineBeforeClass 1 blank line required before class docstring ๐Ÿ› 
D204 OneBlankLineAfterClass 1 blank line required after class docstring ๐Ÿ› 
D205 BlankLineAfterSummary 1 blank line required between summary line and description ๐Ÿ› 
D206 IndentWithSpaces Docstring should be indented with spaces, not tabs
D207 NoUnderIndentation Docstring is under-indented ๐Ÿ› 
D208 NoOverIndentation Docstring is over-indented ๐Ÿ› 
D209 NewLineAfterLastParagraph Multi-line docstring closing quotes should be on a separate line ๐Ÿ› 
D210 NoSurroundingWhitespace No whitespaces allowed surrounding docstring text ๐Ÿ› 
D211 NoBlankLineBeforeClass No blank lines allowed before class docstring ๐Ÿ› 
D212 MultiLineSummaryFirstLine Multi-line docstring summary should start at the first line
D213 MultiLineSummarySecondLine Multi-line docstring summary should start at the second line
D214 SectionNotOverIndented Section is over-indented ("Returns") ๐Ÿ› 
D215 SectionUnderlineNotOverIndented Section underline is over-indented ("Returns") ๐Ÿ› 
D300 UsesTripleQuotes Use """triple double quotes"""
D400 EndsInPeriod First line should end with a period
D402 NoSignature First line should not be the function's signature
D403 FirstLineCapitalized First word of the first line should be properly capitalized
D404 NoThisPrefix First word of the docstring should not be 'This'
D405 CapitalizeSectionName Section name should be properly capitalized ("returns") ๐Ÿ› 
D406 NewLineAfterSectionName Section name should end with a newline ("Returns") ๐Ÿ› 
D407 DashedUnderlineAfterSection Missing dashed underline after section ("Returns") ๐Ÿ› 
D408 SectionUnderlineAfterName Section underline should be in the line following the section's name ("Returns") ๐Ÿ› 
D409 SectionUnderlineMatchesSectionLength Section underline should match the length of its name ("Returns") ๐Ÿ› 
D410 BlankLineAfterSection Missing blank line after section ("Returns") ๐Ÿ› 
D411 BlankLineBeforeSection Missing blank line before section ("Returns") ๐Ÿ› 
D412 NoBlankLinesBetweenHeaderAndContent No blank lines allowed between a section header and its content ("Returns") ๐Ÿ› 
D413 BlankLineAfterLastSection Missing blank line after last section ("Returns") ๐Ÿ› 
D414 NonEmptySection Section has no content ("Returns")
D415 EndsInPunctuation First line should end with a period, question mark, or exclamation point
D416 SectionNameEndsInColon Section name should end with a colon ("Returns") ๐Ÿ› 
D417 DocumentAllArguments Missing argument descriptions in the docstring: x, y
D418 SkipDocstring Function decorated with @overload shouldn't contain a docstring
D419 NonEmpty Docstring is empty

pyupgrade

Code Name Message Fix
U001 UselessMetaclassType __metaclass__ = type is implied ๐Ÿ› 
U002 UnnecessaryAbspath abspath(__file__) is unnecessary in Python 3.9 and later ๐Ÿ› 
U003 TypeOfPrimitive Use str instead of type(...) ๐Ÿ› 
U004 UselessObjectInheritance Class ... inherits from object ๐Ÿ› 
U005 DeprecatedUnittestAlias assertEquals is deprecated, use assertEqual instead ๐Ÿ› 
U006 UsePEP585Annotation Use list instead of List for type annotations ๐Ÿ› 
U007 UsePEP604Annotation Use X | Y for type annotations ๐Ÿ› 
U008 SuperCallWithParameters Use super() instead of super(__class__, self) ๐Ÿ› 

pep8-naming

Code Name Message Fix
N801 InvalidClassName Class name ... should use CapWords convention
N802 InvalidFunctionName Function name ... should be lowercase
N803 InvalidArgumentName Argument name ... should be lowercase
N804 InvalidFirstArgumentNameForClassMethod First argument of a class method should be named cls
N805 InvalidFirstArgumentNameForMethod First argument of a method should be named self
N806 NonLowercaseVariableInFunction Variable ... in function should be lowercase
N807 DunderFunctionName Function name should not start and end with __
N811 ConstantImportedAsNonConstant Constant ... imported as non-constant ...
N812 LowercaseImportedAsNonLowercase Lowercase ... imported as non-lowercase ...
N813 CamelcaseImportedAsLowercase Camelcase ... imported as lowercase ...
N814 CamelcaseImportedAsConstant Camelcase ... imported as constant ...
N815 MixedCaseVariableInClassScope Variable mixedCase in class scope should not be mixedCase
N816 MixedCaseVariableInGlobalScope Variable mixedCase in global scope should not be mixedCase
N817 CamelcaseImportedAsAcronym Camelcase ... imported as acronym ...
N818 ErrorSuffixOnExceptionName Exception name ... should be named with an Error suffix

flake8-comprehensions

Code Name Message Fix
C400 UnnecessaryGeneratorList Unnecessary generator (rewrite as a list comprehension) ๐Ÿ› 
C401 UnnecessaryGeneratorSet Unnecessary generator (rewrite as a set comprehension) ๐Ÿ› 
C402 UnnecessaryGeneratorDict Unnecessary generator (rewrite as a dict comprehension) ๐Ÿ› 
C403 UnnecessaryListComprehensionSet Unnecessary list comprehension (rewrite as a set comprehension) ๐Ÿ› 
C404 UnnecessaryListComprehensionDict Unnecessary list comprehension (rewrite as a dict comprehension)
C405 UnnecessaryLiteralSet Unnecessary (list|tuple) literal (rewrite as a set literal) ๐Ÿ› 
C406 UnnecessaryLiteralDict Unnecessary (list|tuple) literal (rewrite as a dict literal)
C408 UnnecessaryCollectionCall Unnecessary (dict|list|tuple) call (rewrite as a literal) ๐Ÿ› 
C409 UnnecessaryLiteralWithinTupleCall Unnecessary (list|tuple) literal passed to tuple() (remove the outer call to tuple()) ๐Ÿ› 
C410 UnnecessaryLiteralWithinListCall Unnecessary (list|tuple) literal passed to list() (rewrite as a list literal) ๐Ÿ› 
C411 UnnecessaryListCall Unnecessary list call (remove the outer call to list()) ๐Ÿ› 
C413 UnnecessaryCallAroundSorted Unnecessary (list|reversed) call around sorted()
C414 UnnecessaryDoubleCastOrProcess Unnecessary (list|reversed|set|sorted|tuple) call within (list|set|sorted|tuple)()
C415 UnnecessarySubscriptReversal Unnecessary subscript reversal of iterable within (reversed|set|sorted)()
C416 UnnecessaryComprehension Unnecessary (list|set) comprehension (rewrite using (list|set)())
C417 UnnecessaryMap Unnecessary map usage (rewrite using a (list|set|dict) comprehension)

flake8-bugbear

Code Name Message Fix
B002 UnaryPrefixIncrement Python does not support the unary prefix increment.
B006 MutableArgumentDefault Do not use mutable data structures for argument defaults.
B007 UnusedLoopControlVariable Loop control variable i not used within the loop body. ๐Ÿ› 
B011 DoNotAssertFalse Do not assert False (python -O removes these calls), raise AssertionError() ๐Ÿ› 
B013 RedundantTupleInExceptionHandler A length-one tuple literal is redundant. Write except ValueError: instead of except (ValueError,):.
B014 DuplicateHandlerException Exception handler with duplicate exception: ValueError ๐Ÿ› 
B017 NoAssertRaisesException assertRaises(Exception): should be considered evil.
B025 DuplicateTryBlockException try-except block with duplicate exception Exception

flake8-builtins

Code Name Message Fix
A001 BuiltinVariableShadowing Variable ... is shadowing a python builtin
A002 BuiltinArgumentShadowing Argument ... is shadowing a python builtin
A003 BuiltinAttributeShadowing Class attribute ... is shadowing a python builtin

flake8-print

Code Name Message Fix
T201 PrintFound print found ๐Ÿ› 
T203 PPrintFound pprint found ๐Ÿ› 

flake8-quotes

Code Name Message Fix
Q000 BadQuotesInlineString Single quotes found but double quotes preferred
Q001 BadQuotesMultilineString Single quote multiline found but double quotes preferred
Q002 BadQuotesDocstring Single quote docstring found but double quotes preferred
Q003 AvoidQuoteEscape Change outer quotes to avoid escaping inner quotes

Meta rules

Code Name Message Fix
M001 UnusedNOQA Unused noqa directive ๐Ÿ› 

Editor Integrations

PyCharm

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

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.10"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install ruff
      - name: Run Ruff
        run: ruff .

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?

Ruff can be used as a (near) 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 is missing 14 rules related to string .format calls, 1 rule related to docstring parsing, and 1 rule related to redefined variables.

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

Beyond rule-set parity, Ruff suffers from the following limitations vis-ร -vis Flake8:

  1. Ruff does not yet support a few Python 3.9 and 3.10 language features, including structural pattern matching and parenthesized context managers.
  2. Flake8 has a plugin architecture and supports writing custom lint rules. (To date, popular Flake8 plugins have been re-implemented within Ruff directly.)

Which tools does Ruff replace?

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

Ruff also implements the functionality that you get from yesqa, and a subset of the rules implemented in pyupgrade (10/34).

If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, 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.

Does Ruff support NumPy- or Google-style docstrings?

Yes! To enable a specific docstring convention, start by enabling all pydocstyle error codes, and then selectively disabling based on your preferred convention.

For example, if you're coming from flake8-docstrings, the following configuration is equivalent to --docstring-convention=numpy:

[tool.ruff]
extend-select = ["D"]
extend-ignore = [
    "D107",
    "D203",
    "D212",
    "D213",
    "D402",
    "D413",
    "D415",
    "D416",
    "D417",
]

Similarly, the following is equivalent to --docstring-convention=google:

[tool.ruff]
extend-select = ["D"]
extend-ignore = [
    "D203",
    "D204",
    "D213",
    "D215",
    "D400",
    "D404",
    "D406",
    "D407",
    "D408",
    "D409",
    "D413",
]

Similarly, the following is equivalent to --docstring-convention=pep8:

[tool.ruff]
extend-select = ["D"]
extend-ignore = [
    "D203",
    "D212",
    "D213",
    "D214",
    "D215",
    "D404",
    "D405",
    "D406",
    "D407",
    "D408",
    "D409",
    "D410",
    "D411",
    "D413",
    "D415",
    "D416",
    "D417",
]

Development

Ruff is written in Rust (1.64.0). You'll need to install the Rust toolchain for development.

Assuming you have cargo installed, you can run:

cargo run resources/test/fixtures

For development, we use nightly Rust:

cargo +nightly fmt
cargo +nightly clippy
cargo +nightly test

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

Add this pyproject.toml to the CPython directory:

[tool.ruff]
line-length = 88
extend-exclude = [
    "Lib/lib2to3/tests/data/bom.py",
    "Lib/lib2to3/tests/data/crlf.py",
    "Lib/lib2to3/tests/data/different_encoding.py",
    "Lib/lib2to3/tests/data/false_encoding.py",
    "Lib/lib2to3/tests/data/py2_test_grammar.py",
    "Lib/test/bad_coding2.py",
    "Lib/test/badsyntax_3131.py",
    "Lib/test/badsyntax_pep3120.py",
    "Lib/test/encoded_modules/module_iso_8859_1.py",
    "Lib/test/encoded_modules/module_koi8_r.py",
    "Lib/test/test_fstring.py",
    "Lib/test/test_grammar.py",
    "Lib/test/test_importlib/test_util.py",
    "Lib/test/test_named_expressions.py",
    "Lib/test/test_patma.py",
    "Lib/test/test_source_encoding.py",
    "Tools/c-analyzer/c_parser/parser/_delim.py",
    "Tools/i18n/pygettext.py",
    "Tools/test2to3/maintest.py",
    "Tools/test2to3/setup.py",
    "Tools/test2to3/test/test_foo.py",
    "Tools/test2to3/test2to3/hello.py",
]

Next, to benchmark the release build:

cargo build --release

hyperfine --ignore-failure --warmup 10 --runs 100 \
  "./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 ยฑ ฯƒ):     297.4 ms ยฑ   4.9 ms    [User: 2460.0 ms, System: 67.2 ms]
  Range (min โ€ฆ max):   287.7 ms โ€ฆ 312.1 ms    100 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: ./target/release/ruff ./resources/test/cpython/
  Time (mean ยฑ ฯƒ):      79.6 ms ยฑ   7.3 ms    [User: 59.7 ms, System: 356.1 ms]
  Range (min โ€ฆ max):    62.4 ms โ€ฆ 111.2 ms    100 runs

  Warning: Ignoring non-zero exit code.

To benchmark against the ecosystem's existing tools:

hyperfine --ignore-failure --warmup 5 \
  "./target/release/ruff ./resources/test/cpython/ --no-cache" \
  "pylint --recursive=y resources/test/cpython/" \
  "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" \
  "python -m scripts.run_flake8 resources/test/cpython"

In order, these evaluate:

  • Ruff
  • Pylint
  • Pyflakes
  • autoflake
  • pycodestyle
  • Flake8
  • Flake8, with a hack to enable multiprocessing on macOS

(You can poetry install from ./scripts to create a working environment for the above.)

Benchmark 1: ./target/release/ruff ./resources/test/cpython/ --no-cache
  Time (mean ยฑ ฯƒ):     297.9 ms ยฑ   7.0 ms    [User: 2436.6 ms, System: 65.9 ms]
  Range (min โ€ฆ max):   289.9 ms โ€ฆ 314.6 ms    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: pylint --recursive=y resources/test/cpython/
  Time (mean ยฑ ฯƒ):     37.634 s ยฑ  0.225 s    [User: 36.728 s, System: 0.853 s]
  Range (min โ€ฆ max):   37.201 s โ€ฆ 38.106 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 3: pyflakes resources/test/cpython
  Time (mean ยฑ ฯƒ):     40.950 s ยฑ  0.449 s    [User: 40.688 s, System: 0.229 s]
  Range (min โ€ฆ max):   40.348 s โ€ฆ 41.671 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 4: autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython
  Time (mean ยฑ ฯƒ):     11.562 s ยฑ  0.160 s    [User: 107.022 s, System: 1.143 s]
  Range (min โ€ฆ max):   11.417 s โ€ฆ 11.917 s    10 runs

Benchmark 5: pycodestyle resources/test/cpython
  Time (mean ยฑ ฯƒ):     67.428 s ยฑ  0.985 s    [User: 67.199 s, System: 0.203 s]
  Range (min โ€ฆ max):   65.313 s โ€ฆ 68.496 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 6: flake8 resources/test/cpython
  Time (mean ยฑ ฯƒ):     116.099 s ยฑ  1.178 s    [User: 115.217 s, System: 0.845 s]
  Range (min โ€ฆ max):   114.180 s โ€ฆ 117.724 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 7: python -m scripts.run_flake8 resources/test/cpython
  Time (mean ยฑ ฯƒ):     20.477 s ยฑ  0.349 s    [User: 142.372 s, System: 1.504 s]
  Range (min โ€ฆ max):   20.107 s โ€ฆ 21.183 s    10 runs

Summary
  './target/release/ruff ./resources/test/cpython/ --no-cache' ran
   38.81 ยฑ 1.05 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
   68.74 ยฑ 1.99 times faster than 'python -m scripts.run_flake8 resources/test/cpython'
  126.33 ยฑ 3.05 times faster than 'pylint --recursive=y resources/test/cpython/'
  137.46 ยฑ 3.55 times faster than 'pyflakes resources/test/cpython'
  226.35 ยฑ 6.23 times faster than 'pycodestyle resources/test/cpython'
  389.73 ยฑ 9.92 times faster than 'flake8 resources/test/cpython'

License

MIT

Contributing

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

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ruff-0.0.98.tar.gz (231.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ruff-0.0.98-py3-none-win_amd64.whl (3.1 MB view details)

Uploaded Python 3Windows x86-64

ruff-0.0.98-py3-none-win32.whl (3.0 MB view details)

Uploaded Python 3Windows x86

ruff-0.0.98-py3-none-musllinux_1_2_x86_64.whl (3.4 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

ruff-0.0.98-py3-none-musllinux_1_2_i686.whl (3.3 MB view details)

Uploaded Python 3musllinux: musl 1.2+ i686

ruff-0.0.98-py3-none-musllinux_1_2_armv7l.whl (3.0 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARMv7l

ruff-0.0.98-py3-none-musllinux_1_2_aarch64.whl (3.1 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

ruff-0.0.98-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

ruff-0.0.98-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl (3.0 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ s390x

ruff-0.0.98-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (2.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ppc64le

ruff-0.0.98-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl (2.8 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ppc64

ruff-0.0.98-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl (3.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ i686

ruff-0.0.98-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (2.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARMv7l

ruff-0.0.98-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.1 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

ruff-0.0.98-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl (6.3 MB view details)

Uploaded Python 3macOS 10.9+ universal2 (ARM64, x86-64)macOS 10.9+ x86-64macOS 11.0+ ARM64

ruff-0.0.98-py3-none-macosx_10_7_x86_64.whl (3.3 MB view details)

Uploaded Python 3macOS 10.7+ x86-64

File details

Details for the file ruff-0.0.98.tar.gz.

File metadata

  • Download URL: ruff-0.0.98.tar.gz
  • Upload date:
  • Size: 231.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.8.10

File hashes

Hashes for ruff-0.0.98.tar.gz
Algorithm Hash digest
SHA256 b0b5f2f6b325e7cdd9bf9dc035bf1a35bd15726e12f224253ff1e3ccee8500d0
MD5 401778d189913c3065bb1ad025503685
BLAKE2b-256 4703d97ee64c6c8899eed05f51278ea3a1de7aec597d746afa99b689f7e91c3c

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-win_amd64.whl.

File metadata

  • Download URL: ruff-0.0.98-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.1 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.8.10

File hashes

Hashes for ruff-0.0.98-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 245e1755b77a98018b9b5414cf9838d084a35e042093b2d10d5b3488407e661a
MD5 4178762216e9c15471f46147db6f3c84
BLAKE2b-256 f37a9bd09a5dede74f61f86bb11a1904d7ed7434966b5884305f33726637459d

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-win32.whl.

File metadata

  • Download URL: ruff-0.0.98-py3-none-win32.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: Python 3, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.8.10

File hashes

Hashes for ruff-0.0.98-py3-none-win32.whl
Algorithm Hash digest
SHA256 18216f3adbe62bd69f822d6c35846b4378fdbf5446199cfe299e36bc64b20c6e
MD5 ee7ef7199f338d892bd6e80dbbc8c15a
BLAKE2b-256 4b88f0608c6c9b3a48650d23084b4a0a68f8e99946b78126998414c712c0568c

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 aa67b96ac60259f35dbdbea53f6dac4621c1fe395719b2a41a586997156ed748
MD5 5be93d1a0bd002031a73e02cc4628172
BLAKE2b-256 f13e729a19df9c7d87657d4d5e6164a2d9ac8f5bcd4bd461de3809ba751866cd

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-musllinux_1_2_i686.whl.

File metadata

  • Download URL: ruff-0.0.98-py3-none-musllinux_1_2_i686.whl
  • Upload date:
  • Size: 3.3 MB
  • Tags: Python 3, musllinux: musl 1.2+ i686
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.8.10

File hashes

Hashes for ruff-0.0.98-py3-none-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 557e6614261de1026089091ff5cebb847731b88c267fbd365f761aeaeaa071b3
MD5 cd1512d942f1cf6537a156ebe9f89afb
BLAKE2b-256 a0e34b0c9233547584a23615443f51dc8a107f8915f973375624549b2f155691

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 63377ab99a7162543b83bbfb1d3955eafafad934cad5c673406df72d7510f060
MD5 1190e989ec004df5cdb49960c9e7b9b5
BLAKE2b-256 c380b2985d9dca72aa526b614abe434b0bb49a9a5145f761fc09e8125ddffca0

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 2fa0cf4641cd970949141b16772e7a7c1e11cc60e4402f20e3598c6b0b9298c4
MD5 c8f62749b6666075e788f4b1b3c6c7dd
BLAKE2b-256 10818a071232ce8004a1350d46d907462288bb67655315a8971aee3de4951ff3

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a62ae59e16a29b8d3bee799338929f0d24e430386156903ff3431a0e509cd9d6
MD5 547d5f6f66e7be4ee1eed10763f860bd
BLAKE2b-256 8a45f2cbf0c0894c9577964da75a07d73933480a9f020f635499d18d817e46f9

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 ea830b03be2c902fb8686fd58ac763dea19d3b59a67bd92221ee571faa1b8363
MD5 5bc3a1c3b8786259eed3e4e044326b7f
BLAKE2b-256 b8c4747df5f840a547de95ff4edc2f798fd06985dd03c031bd32b47273b1d96c

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 1329f7e7d0497d5a1b752cd58c89ad184a86f39a821d46b44c9ae9360928abbf
MD5 ce35e0d632dbb21b16031b22cde1f2d5
BLAKE2b-256 a729b7ed2b92430e5f1783a03e6553a9d91bdf0bc371898e52558ef15344089b

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl
Algorithm Hash digest
SHA256 99bb44aac9777aadd95457f58275a35642bdba841d649079f8e900e4f5670bf8
MD5 ddbe630e526970849eb3784ae4d37e49
BLAKE2b-256 037913a360adf7c95e94fa3141c3e3609f9c9d2a6fb8adda0359b8cb7ab19a7f

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 613f609fdecb506f35319dcd1918997edc3267d15d7343cb134e21a5a94ba46b
MD5 e29931ea9bb7704cb33d905462a6fea2
BLAKE2b-256 234d6e3f91131d8d17971086606cb92f6019916a8f730d7ac560afb6465128e7

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 1306fc0c0b99362817e17e27f062ae01d93d8dfa022dc1ded828616661440e6f
MD5 7fdfed8f3de37c1f7a6b4321968986b3
BLAKE2b-256 ca99ba6e2ec29715bfe4fb8e509bf7765f2eca10da130fe36118f9024a8f0ea3

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 476378325d5586eb4dec1b8663e6e33a8a125afa82a3f54fd837a5c5055d407b
MD5 924ababfaa17967332f8c5b83d3f8992
BLAKE2b-256 9d021608539cbacf6906e7cacd0a1d8b623f3e9db450d9bfdc2184de02398ddb

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 7151db2f2de5edae715850d0abdea0e5fd2dc40ed79f7ec839ebf96a96332510
MD5 420b20dd9f2168171a425c16105ab3cf
BLAKE2b-256 5365651eabf7c48a1a8448779b9f76e50a8c1c74a46f279a81ff133b6e91e3b8

See more details on using hashes here.

File details

Details for the file ruff-0.0.98-py3-none-macosx_10_7_x86_64.whl.

File metadata

File hashes

Hashes for ruff-0.0.98-py3-none-macosx_10_7_x86_64.whl
Algorithm Hash digest
SHA256 35c655078debdb26ee532b857cca4aea9d18e796d3f3196a44e14725d18ea1b6
MD5 6c755702b35f4acbdf315b53e81e3a41
BLAKE2b-256 26f43b4e5e1e75e232ee4b3486ab49ea0780d129fae939a79e468ab8d7681d0a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page