Skip to main content

An extremely fast Python linter, written in Rust.

Project description

ruff

Actions status PyPI version

An extremely fast Python linter, written in Rust.

Bar chart with benchmark results

Linting the CPython codebase from scratch.

Major features:

  • 10-100x faster than existing linters.
  • Installable via pip.
  • Python 3.10 compatibility.
  • ESLint-inspired cache semantics.
  • TypeScript-inspired --watch semantics.
  • pyproject.toml support.

ruff is a proof-of-concept and not yet intended for production use. It supports only a small subset of the Flake8 rules, and may crash on your codebase.

Installation and usage

Installation

Available as ruff on PyPI:

pip install ruff

For now, wheels are available for Python 3.7, 3.8, 3.9, and 3.10 on macOS, Windows, and Linux. If a wheel isn't available for your Python version or platform, you'll need to install the Rust toolchain prior to running pip install ruff. (This is an effort limitation on my part, not a technical limitation.)

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

Configuration

ruff is configurable both via pyproject.toml and the command line.

For example, you could configure ruff to only enforce a subset of rules with:

[tool.ruff]
line-length = 88
select = [
    "F401",
    "F403",
]

Alternatively, on the command-line:

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

See ruff --help for more:

ruff
A Python linter written in Rust

USAGE:
    ruff [OPTIONS] <FILES>...

ARGS:
    <FILES>...

OPTIONS:
    -e, --exit-zero             Exit with status code "0", even upon detecting errors
    -h, --help                  Print help information
        --ignore <IGNORE>...    Comma-separated list of error codes to ignore
    -n, --no-cache              Disable cache reads
    -q, --quiet                 Disable all logging (but still exit with status code "1" upon
                                detecting errors)
        --select <SELECT>...    Comma-separated list of error codes to enable
    -v, --verbose               Enable verbose logging
    -w, --watch                 Run in watch mode by re-running whenever files change

Development

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

Assuming you have cargo installed, you can run:

cargo run resources/test/src
cargo fmt
cargo clippy
cargo test

Deployment

ruff is distributed on PyPI, and published via maturin.

See: .github/workflows/release.yaml.

Benchmarking

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.linter]
line-length = 88
exclude = [
    "Lib/ctypes/test/test_numbers.py",
    "Lib/dataclasses.py",
    "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/sqlite3/test/factory.py",
    "Lib/sqlite3/test/hooks.py",
    "Lib/sqlite3/test/regression.py",
    "Lib/sqlite3/test/transactions.py",
    "Lib/sqlite3/test/types.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/sortperf.py",
    "Lib/test/test_email/torture_test.py",
    "Lib/test/test_fstring.py",
    "Lib/test/test_genericpath.py",
    "Lib/test/test_getopt.py",
    "Lib/test/test_grammar.py",
    "Lib/test/test_htmlparser.py",
    "Lib/test/test_importlib/stubs.py",
    "Lib/test/test_importlib/test_files.py",
    "Lib/test/test_importlib/test_metadata_api.py",
    "Lib/test/test_importlib/test_open.py",
    "Lib/test/test_importlib/test_util.py",
    "Lib/test/test_named_expressions.py",
    "Lib/test/test_patma.py",
    "Lib/test/test_peg_generator/__main__.py",
    "Lib/test/test_pipes.py",
    "Lib/test/test_source_encoding.py",
    "Lib/test/test_weakref.py",
    "Lib/test/test_webbrowser.py",
    "Lib/tkinter/__main__.py",
    "Lib/tkinter/test/test_tkinter/test_variables.py",
    "Modules/_decimal/libmpdec/literature/fnt.py",
    "Modules/_decimal/tests/deccheck.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 1 \
  "./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 ± σ):     353.6 ms ±   7.6 ms    [User: 2868.8 ms, System: 171.5 ms]
  Range (min  max):   344.4 ms  367.3 ms    10 runs

Benchmark 2: ./target/release/ruff ./resources/test/cpython/
  Time (mean ± σ):      59.6 ms ±   2.5 ms    [User: 36.4 ms, System: 345.6 ms]
  Range (min  max):    55.9 ms   67.0 ms    48 runs

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" \
  "pycodestyle --select E501 resources/test/cpython" \
  "flake8 resources/test/cpython" \
  "flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython" \
  "python -m scripts.run_flake8 resources/test/cpython" \
  "python -m scripts.run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501"

In order, these evaluate:

  • ruff
  • Pylint
  • PyFlakes
  • autoflake
  • pycodestyle
  • pycodestyle, limited to the checks supported by ruff
  • Flake8
  • Flake8, limited to the checks supported by ruff
  • Flake8, with a hack to enable multiprocessing on macOS
  • Flake8, with a hack to enable multiprocessing on macOS, limited to the checks supported by ruff

(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 ± σ):     469.3 ms ±  16.3 ms    [User: 2663.0 ms, System: 972.5 ms]
  Range (min  max):   445.2 ms  494.8 ms    10 runs

Benchmark 2: pylint --recursive=y resources/test/cpython/
  Time (mean ± σ):     27.211 s ±  0.097 s    [User: 26.405 s, System: 0.799 s]
  Range (min  max):   27.056 s  27.349 s    10 runs

Benchmark 3: pyflakes resources/test/cpython
  Time (mean ± σ):     27.309 s ±  0.033 s    [User: 27.137 s, System: 0.169 s]
  Range (min  max):   27.267 s  27.372 s    10 runs

Benchmark 4: autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython
  Time (mean ± σ):      8.027 s ±  0.024 s    [User: 74.255 s, System: 0.953 s]
  Range (min  max):    7.969 s   8.052 s    10 runs

Benchmark 5: pycodestyle resources/test/cpython
  Time (mean ± σ):     41.666 s ±  0.266 s    [User: 41.531 s, System: 0.132 s]
  Range (min  max):   41.295 s  41.980 s    10 runs

Benchmark 6: pycodestyle --select E501 resources/test/cpython
  Time (mean ± σ):     14.547 s ±  0.077 s    [User: 14.466 s, System: 0.079 s]
  Range (min  max):   14.429 s  14.695 s    10 runs

Benchmark 7: flake8 resources/test/cpython
  Time (mean ± σ):     75.700 s ±  0.152 s    [User: 75.254 s, System: 0.440 s]
  Range (min  max):   75.513 s  76.014 s    10 runs

Benchmark 8: flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython
  Time (mean ± σ):     75.122 s ±  0.532 s    [User: 74.677 s, System: 0.440 s]
  Range (min  max):   74.130 s  75.606 s    10 runs

Benchmark 9: python -m scripts.run_flake8 resources/test/cpython
  Time (mean ± σ):     12.794 s ±  0.147 s    [User: 90.792 s, System: 0.738 s]
  Range (min  max):   12.606 s  13.030 s    10 runs

Benchmark 10: python -m scripts.run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501
  Time (mean ± σ):     12.487 s ±  0.118 s    [User: 90.052 s, System: 0.714 s]
  Range (min  max):   12.265 s  12.665 s    10 runs

Summary
  './target/release/ruff ./resources/test/cpython/ --no-cache' ran
   17.10 ± 0.60 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
   26.60 ± 0.96 times faster than 'python -m scripts.run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501'
   27.26 ± 1.00 times faster than 'python -m scripts.run_flake8 resources/test/cpython'
   30.99 ± 1.09 times faster than 'pycodestyle --select E501 resources/test/cpython'
   57.98 ± 2.03 times faster than 'pylint --recursive=y resources/test/cpython/'
   58.19 ± 2.02 times faster than 'pyflakes resources/test/cpython'
   88.77 ± 3.14 times faster than 'pycodestyle resources/test/cpython'
  160.06 ± 5.68 times faster than 'flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython'
  161.29 ± 5.61 times faster than 'flake8 resources/test/cpython'

License

MIT

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.19.tar.gz (69.8 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.19-py3-none-win_amd64.whl (1.7 MB view details)

Uploaded Python 3Windows x86-64

ruff-0.0.19-py3-none-win32.whl (1.6 MB view details)

Uploaded Python 3Windows x86

ruff-0.0.19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl (2.1 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ s390x

ruff-0.0.19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (1.9 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ppc64le

ruff-0.0.19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARMv7l

ruff-0.0.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

ruff-0.0.19-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.8 MB view details)

Uploaded Python 3manylinux: glibc 2.12+ x86-64

ruff-0.0.19-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl (1.8 MB view details)

Uploaded Python 3manylinux: glibc 2.12+ i686

ruff-0.0.19-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl (3.2 MB view details)

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

ruff-0.0.19-py3-none-macosx_10_7_x86_64.whl (1.7 MB view details)

Uploaded Python 3macOS 10.7+ x86-64

File details

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

File metadata

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

File hashes

Hashes for ruff-0.0.19.tar.gz
Algorithm Hash digest
SHA256 eaf156825395c9adf492fe162d66e41a68877f47f97dbf25fce8c2bf578747d4
MD5 082ca555ce4df259f780211cb93b3e39
BLAKE2b-256 c005a176b3e7a56db8baa61bcf3c59a076142f920b965dce4bad52a13e3059ef

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for ruff-0.0.19-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 51d334d84d120ecfe6fec6000cf27e37fab948670197d2c153f35ea8712d7c00
MD5 73d71f195ed1e20fd68146724d03a606
BLAKE2b-256 2284bada6a05bb268deb121ca1851902ae91a3818feebb0c8645f715a02ef6b6

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for ruff-0.0.19-py3-none-win32.whl
Algorithm Hash digest
SHA256 cb37afb065a5e70dff780c08b958d054460f8dd0aad22e725843f9c021b059e8
MD5 2dea80f889a592b9b6818d2ce3ac8fce
BLAKE2b-256 fe174a8b37a52e61bb951aa061dc750e86c13734967f8d438706af33d907c9a8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 8dd3ed94e4fb2541a411f047d62960e3559309780b0bb8c56326be14d9e73778
MD5 77c2b2cb13a3064bc2dd9a4a76baad63
BLAKE2b-256 6f0c2a223414854fdbbec7009027693aa892f7c6b636b7b5e4b60d61eee0d3b3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 de24e09ecc61ea22f86c81ba019c92e081faa937b10c2a5fa80ec85325136c75
MD5 36ea81d2083f9ae281dba1c901d47845
BLAKE2b-256 b39e7471f73c7a8a6637bd4ae6792c8f417d9f7487175cea02281826783cca76

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 e59e46bbaf87cb8eb5983a25d9d1d50f4f627b66ed5222e6e51feb45fffbb80a
MD5 b3b8626b0b27ccc9ae1e2bcca6f7cd5a
BLAKE2b-256 511579035de2352edade74261a4633fd39e6d37df269b5f341cdfc39a316a018

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0e523c517dbda60f02d0149208cc3091aa5e018a562427338fa90c07107c77ce
MD5 7c36f97dadc362c740cad56c8b2d5f9c
BLAKE2b-256 01cf020c2ab525cfe50642a72483a5087f178f883f42359d7e94d9754876159e

See more details on using hashes here.

File details

Details for the file ruff-0.0.19-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl.

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm Hash digest
SHA256 f266762f3482b6c542f12f87b0e9b8cfaea16d4727a2d044b95e68a52164a756
MD5 c17150b728daa2d712e7b343dfb91c5f
BLAKE2b-256 f34c3fc70a93db074c4094ecef7343f1c17f5e5f59f172c255e946974545fd8c

See more details on using hashes here.

File details

Details for the file ruff-0.0.19-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl.

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm Hash digest
SHA256 4f9f817988964fb83a98a838b8750bfa6dce08e178f9b7be6d61908a5abde34b
MD5 76e500a63b4a2bf8e512fc5e3a1c27c6
BLAKE2b-256 86af325bd20cf692b06112b2b80216ba33aabd0d64522bdfe9bee622cfee5c30

See more details on using hashes here.

File details

Details for the file ruff-0.0.19-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.19-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 e0a993782509d70b3b09183e4c07f500e86c29b0a3d28534d84d10e2e8a70deb
MD5 76fabd1f0e8db98e6e6c6c54caeac3fa
BLAKE2b-256 ba9a0468976d3d56890335c05791c7dd1639e3c49c39c957603a8a25b9ed391d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ruff-0.0.19-py3-none-macosx_10_7_x86_64.whl
Algorithm Hash digest
SHA256 022da3312e5c00b9811a324edff8fd7b433985e24266f85f830715946a565302
MD5 0d9477ef8aabc765400722fef1a35f6a
BLAKE2b-256 dbc37605e3ecaa27b28c23e77c106c24ebea0010b93b75f7e8367e823ae02a38

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