ruff
A performance-focused, Pyflakes-inspired Python linter, written in Rust.
Features:
- Python 3.10 compatibility
- ESLint-inspired cache semantics
- TypeScript-inspired
--watchsemantics pyproject.tomlsupport
Installation
Available as ruff on PyPI:
pip install ruff
Usage
To run the linter, try any of the following:
ruff path/to/code/to/check.py
# ...or...
ruff path/to/code/
# ...or...
ruff path/to/code/*.py
You can also run in --watch mode to automatically re-run the linter on-change with, e.g.:
ruff path/to/code/ --watch
Development
ruff is written in Rust:
cargo fmt
cargo clippy
cargo run resources/test/src
Deployment
ruff is released for Python using maturin:
maturin publish --username crmarsh --skip-existing --target x86_64-apple-darwin && \
maturin publish --username crmarsh --skip-existing --target aarch64-apple-darwin
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 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 5 \
"./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 the ecosystem's existing tools:
python -m venv .venv
source .venv/bin/activate
pip install pylint pycodestyle flake8 autoflake pyflakes
hyperfine --ignore-failure --warmup 5 \
"pycodestyle resources/test/cpython" \
"pyflakes resources/test/cpython" \
"flake8 resources/test/cpython" \
"autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
"pylint --recursive=y resources/test/cpython/" \
"pycodestyle --select E501 resources/test/cpython" \
"flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython" \
"python -m run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501" \
"python -m run_flake8 resources/test/cpython"
∴ hyperfine --ignore-failure --warmup 5 \
→ "pycodestyle resources/test/cpython" \
→ "pyflakes resources/test/cpython" \
→ "flake8 resources/test/cpython" \
→ "autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython" \
→ "pylint --recursive=y resources/test/cpython/" \
→ "pycodestyle --select E501 resources/test/cpython" \
→ "flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython" \
→ "python -m run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501" \
→ "python -m run_flake8 resources/test/cpython"
Benchmark 1: pycodestyle resources/test/cpython
Time (mean ± σ): 41.921 s ± 1.409 s [User: 41.451 s, System: 0.194 s]
Range (min … max): 41.182 s … 45.894 s 10 runs
Warning: Ignoring non-zero exit code.
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet PC without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Benchmark 2: pyflakes resources/test/cpython
Time (mean ± σ): 27.960 s ± 1.251 s [User: 27.491 s, System: 0.236 s]
Range (min … max): 26.449 s … 29.899 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 3: flake8 resources/test/cpython
Time (mean ± σ): 75.320 s ± 0.909 s [User: 74.625 s, System: 0.610 s]
Range (min … max): 74.181 s … 77.336 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 ± σ): 32.690 s ± 0.585 s [User: 32.300 s, System: 0.296 s]
Range (min … max): 31.948 s … 33.326 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 5: pylint --recursive=y resources/test/cpython/
Time (mean ± σ): 27.592 s ± 0.227 s [User: 26.627 s, System: 0.911 s]
Range (min … max): 27.325 s … 27.955 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 6: pycodestyle --select E501 resources/test/cpython
Time (mean ± σ): 14.540 s ± 0.156 s [User: 14.397 s, System: 0.121 s]
Range (min … max): 14.384 s … 14.920 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 7: flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython
Time (mean ± σ): 75.391 s ± 0.600 s [User: 74.642 s, System: 0.591 s]
Range (min … max): 74.458 s … 76.550 s 10 runs
Warning: Ignoring non-zero exit code.
Benchmark 8: python -m run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501
Time (mean ± σ): 12.542 s ± 0.467 s [User: 87.891 s, System: 0.816 s]
Range (min … max): 11.771 s … 13.034 s 10 runs
Benchmark 9: python -m run_flake8 resources/test/cpython
Time (mean ± σ): 12.276 s ± 0.398 s [User: 86.720 s, System: 0.792 s]
Range (min … max): 11.809 s … 12.865 s 10 runs
Summary
'python -m run_flake8 resources/test/cpython' ran
1.02 ± 0.05 times faster than 'python -m run_flake8 resources/test/cpython --select=F831,F541,F634,F403,F706,F901,E501'
1.18 ± 0.04 times faster than 'pycodestyle --select E501 resources/test/cpython'
2.25 ± 0.08 times faster than 'pylint --recursive=y resources/test/cpython/'
2.28 ± 0.13 times faster than 'pyflakes resources/test/cpython'
2.66 ± 0.10 times faster than 'autoflake --recursive --expand-star-imports --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys resources/test/cpython'
3.41 ± 0.16 times faster than 'pycodestyle resources/test/cpython'
6.14 ± 0.21 times faster than 'flake8 resources/test/cpython'
6.14 ± 0.21 times faster than 'flake8 --select=F831,F541,F634,F403,F706,F901,E501 resources/test/cpython'
Metadata
Release files for ruff 0.0.15
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ruff-0.0.15.tar.gz | 51.7 kB | Without provenance | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ruff-0.0.15-cp38-cp38-macosx_11_0_arm64.whl | CPython 3.8 | CPython 3.8 | macOS 11.0+ ARM64 | Details |
| ruff-0.0.15-cp38-cp38-macosx_10_7_x86_64.whl | CPython 3.8 | CPython 3.8 | macOS 10.7+ x86-64 | Details |
Total release size: 3.3 MB
Release files / ruff-0.0.15.tar.gz
| Download URL | ruff-0.0.15.tar.gz |
|---|---|
| Size | 51.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b255e451e3cbab4b9ae953afb1e1c9070588d01510a9b546a1e9418137dd3968
|
|
BLAKE2b-256 checksum How to use checksums |
07bda03aaa38296b510b4764fcf71afa4ce97ae733211b4e64b2d224e2482a75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/0.13.0
|
Release files / ruff-0.0.15-cp38-cp38-macosx_11_0_arm64.whl
| Download URL | ruff-0.0.15-cp38-cp38-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.8 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c12496da33a3e24c3bce8a5b0ed9eaa33bb15ff37e552366bb479809023e8a24
|
|
BLAKE2b-256 checksum How to use checksums |
5c0465e2c32630578c9e1e86b77100313d2436f3d10ff9fb5515d98df8b5ab8e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/0.13.0
|
Release files / ruff-0.0.15-cp38-cp38-macosx_10_7_x86_64.whl
| Download URL | ruff-0.0.15-cp38-cp38-macosx_10_7_x86_64.whl |
|---|---|
| Size | 1.7 MB |
| Tags | CPython 3.8 macOS 10.7+ x86-64 |
|
SHA-256 checksum How to use checksums |
b0260a7426aae275cbf5f6a6f38c50adbfa07f7043b22dc19aadcfc00b0a03c3
|
|
BLAKE2b-256 checksum How to use checksums |
b471ea2c9a86d2bfef1c1e2d221c0d00592d731ce695372feae7f824a5b57122
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/0.13.0
|
Security for ruff 0.0.15
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.