Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56f69ce71e | ||
|
|
248a6cd50b | ||
|
|
d9e659d817 | ||
|
|
77e5564f4b | ||
|
|
e79766d5ec | ||
|
|
c55fd76743 | ||
|
|
e2aedc5ba8 | ||
|
|
1b2d085460 | ||
|
|
cb138526b1 | ||
|
|
8eac270d8f | ||
|
|
6e19fd20bb | ||
|
|
10868445f5 | ||
|
|
e3ecf21287 | ||
|
|
fd849e112e | ||
|
|
af27471c77 | ||
|
|
bb466bc8d3 | ||
|
|
7741a713e2 | ||
|
|
3ab1cfc6f8 | ||
|
|
e73f13473d |
133
Cargo.lock
generated
133
Cargo.lock
generated
@@ -418,9 +418,9 @@ checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.9"
|
||||
version = "4.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30607dd93c420c6f1f80b544be522a0238a7db35e6a12968d28910983fee0df0"
|
||||
checksum = "6bf8832993da70a4c6d13c581f4463c2bdda27b9bf1c5498dc4365543abe6d6f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
@@ -433,9 +433,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.9"
|
||||
version = "4.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a307492e1a34939f79d3b6b9650bd2b971513cd775436bf2b78defeb5af00b"
|
||||
checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
@@ -466,6 +466,16 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
@@ -588,6 +598,50 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
@@ -954,8 +1008,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1020,17 +1076,28 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.50"
|
||||
version = "0.1.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0"
|
||||
checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
@@ -1114,9 +1181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "joinery"
|
||||
@@ -1228,9 +1295,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
@@ -1256,6 +1323,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
@@ -1541,18 +1617,18 @@ checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
|
||||
[[package]]
|
||||
name = "path-absolutize"
|
||||
version = "3.0.13"
|
||||
version = "3.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3de4b40bd9736640f14c438304c09538159802388febb02c8abaae0846c1f13"
|
||||
checksum = "0f1d4993b16f7325d90c18c3c6a3327db7808752db8d208cea0acee0abd52c52"
|
||||
dependencies = [
|
||||
"path-dedot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "path-dedot"
|
||||
version = "3.0.17"
|
||||
version = "3.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d611d5291372b3738a34ebf0d1f849e58b1dcc1101032f76a346eaa1f8ddbb5b"
|
||||
checksum = "9a81540d94551664b72b72829b12bd167c73c9d25fbac0e04fafa8023f7e4901"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
@@ -1969,7 +2045,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.75"
|
||||
version = "0.0.77"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -1983,6 +2059,7 @@ dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"fern",
|
||||
"filetime",
|
||||
"getrandom 0.2.7",
|
||||
"glob",
|
||||
"insta",
|
||||
"itertools",
|
||||
@@ -2024,7 +2101,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f#778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=210db77e4274787028dc3ebc0b4841d1334c8c10#210db77e4274787028dc3ebc0b4841d1334c8c10"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2034,7 +2111,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f#778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=210db77e4274787028dc3ebc0b4841d1334c8c10#210db77e4274787028dc3ebc0b4841d1334c8c10"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -2057,7 +2134,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f#778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=210db77e4274787028dc3ebc0b4841d1334c8c10#210db77e4274787028dc3ebc0b4841d1334c8c10"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2074,7 +2151,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f#778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f"
|
||||
source = "git+https://github.com/charliermarsh/RustPython.git?rev=210db77e4274787028dc3ebc0b4841d1334c8c10#210db77e4274787028dc3ebc0b4841d1334c8c10"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2122,6 +2199,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
@@ -2160,9 +2243,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.85"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -2351,9 +2434,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2619,9 +2702,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.75"
|
||||
version = "0.0.77"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@@ -9,10 +9,8 @@ name = "ruff"
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.60" }
|
||||
bincode = { version = "1.3.3" }
|
||||
cacache = { version = "10.0.1" }
|
||||
chrono = { version = "0.4.21" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
clearscreen = { version = "1.0.10" }
|
||||
colored = { version = "2.0.0" }
|
||||
common-path = { version = "1.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
@@ -25,12 +23,12 @@ log = { version = "0.4.17" }
|
||||
notify = { version = "4.0.17" }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
once_cell = { version = "1.13.1" }
|
||||
path-absolutize = { version = "3.0.13", features = ["once_cell_cache"] }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
rayon = { version = "1.5.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/charliermarsh/RustPython.git", rev = "778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f" }
|
||||
rustpython-common = { git = "https://github.com/charliermarsh/RustPython.git", rev = "778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/charliermarsh/RustPython.git", rev = "778ae2aeb521d0438d2a91bd11238bb5c2bf9d4f" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/charliermarsh/RustPython.git", rev = "210db77e4274787028dc3ebc0b4841d1334c8c10" }
|
||||
rustpython-common = { git = "https://github.com/charliermarsh/RustPython.git", rev = "210db77e4274787028dc3ebc0b4841d1334c8c10" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/charliermarsh/RustPython.git", rev = "210db77e4274787028dc3ebc0b4841d1334c8c10" }
|
||||
serde = { version = "1.0.143", features = ["derive"] }
|
||||
serde_json = { version = "1.0.83" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
@@ -41,6 +39,15 @@ toml = { version = "0.5.9" }
|
||||
update-informer = { version = "0.5.0", default_features = false, features = ["pypi"], optional = true }
|
||||
walkdir = { version = "2.3.2" }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
cacache = { version = "10.0.1" } # uses async-std
|
||||
clearscreen = { version = "1.0.10" } # uses which
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
# For (future) wasm-pack support
|
||||
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { version = "2.0.4" }
|
||||
insta = { version = "1.19.1", features = ["yaml"] }
|
||||
|
||||
559
README.md
559
README.md
@@ -1,4 +1,4 @@
|
||||
# ruff
|
||||
# Ruff
|
||||
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
@@ -20,17 +20,37 @@ An extremely fast Python linter, written in Rust.
|
||||
- 🤝 Python 3.10 compatibility
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 📦 [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#caching)-inspired cache support
|
||||
- 🔧 [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix)-inspired `--fix` support
|
||||
- 👀 [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)-inspired `--watch` support
|
||||
- ⚖️ [Near-complete parity](#Parity-with-Flake8) with the built-in Flake8 rule set
|
||||
- 🔧 [ESLint](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix)-inspired autofix support (e.g., automatically remove unused imports)
|
||||
- 👀 [TypeScript](https://www.typescriptlang.org/docs/handbook/configuring-watch.html)-inspired `--watch` support, for continuous file monitoring
|
||||
- ⚖️ [Near-parity](#Parity-with-Flake8) with the built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/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`](https://pypi.org/project/pydocstyle/), [`yesqa`](https://github.com/asottile/yesqa),
|
||||
and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/) and [`autoflake`](https://pypi.org/project/autoflake/)
|
||||
all while executing tens or hundreds of times faster than any individual tool.
|
||||
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
|
||||
## Installation and usage
|
||||
## Table of Contents
|
||||
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
2. [Configuration](#configuration)
|
||||
3. [Supported Rules](#supported-rules)
|
||||
4. [Editor Integrations](#editor-integrations)
|
||||
5. [FAQ](#faq)
|
||||
6. [Development](#development)
|
||||
7. [Releases](#releases)
|
||||
8. [Benchmarks](#benchmarks)
|
||||
9. [License](#license)
|
||||
10. [Contributing](#contributing)
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
### Installation
|
||||
|
||||
Available as [ruff](https://pypi.org/project/ruff/) on PyPI:
|
||||
Available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
|
||||
```shell
|
||||
pip install ruff
|
||||
@@ -38,7 +58,7 @@ pip install ruff
|
||||
|
||||
### Usage
|
||||
|
||||
To run ruff, try any of the following:
|
||||
To run Ruff, try any of the following:
|
||||
|
||||
```shell
|
||||
ruff path/to/code/to/check.py
|
||||
@@ -46,27 +66,27 @@ ruff path/to/code/
|
||||
ruff path/to/code/*.py
|
||||
```
|
||||
|
||||
You can run ruff in `--watch` mode to automatically re-run on-change:
|
||||
You can run Ruff in `--watch` mode to automatically re-run on-change:
|
||||
|
||||
```shell
|
||||
ruff path/to/code/ --watch
|
||||
```
|
||||
|
||||
ruff also works with [pre-commit](https://pre-commit.com):
|
||||
Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.75
|
||||
rev: v0.0.77
|
||||
hooks:
|
||||
- id: lint
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
ruff is configurable both via `pyproject.toml` and the command line.
|
||||
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:
|
||||
For example, you could configure Ruff to only enforce a subset of rules with:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
@@ -158,7 +178,7 @@ Exclusions are based on globs, and can be either:
|
||||
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](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
|
||||
To ignore an error in-line, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
|
||||
To ignore an individual error, add `# noqa: {code}` to the end of the line, like so:
|
||||
|
||||
```python
|
||||
@@ -182,201 +202,218 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i
|
||||
""" # noqa: E501
|
||||
```
|
||||
|
||||
ruff supports several (experimental) workflows to aid in `noqa` management.
|
||||
Ruff supports several workflows to aid in `noqa` management.
|
||||
|
||||
First, ruff provides a special error code, `M001`, to enforce that your `noqa` directives are
|
||||
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.
|
||||
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
|
||||
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.**
|
||||
|
||||
### Compatibility with Black
|
||||
## Supported Rules
|
||||
|
||||
ruff is compatible with [Black](https://github.com/psf/black) out-of-the-box, as long as
|
||||
the `line-length` setting is consistent between the two.
|
||||
By default, Ruff enables all `E`, `W`, and `F` error codes, which correspond to those built-in to
|
||||
Flake8.
|
||||
|
||||
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.
|
||||
### Pyflakes
|
||||
|
||||
### Parity with Flake8
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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/method |
|
||||
| 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` |
|
||||
|
||||
ruff's goal is to achieve feature parity with Flake8 when used (1) without plugins, (2) alongside
|
||||
Black, and (3) on Python 3 code.
|
||||
### pycodestyle
|
||||
|
||||
**Under those conditions, ruff implements 44 out of 60 rules.** (ruff is missing: 14 rules related
|
||||
to string `.format` calls, 1 rule related to docstring parsing, and 1 rule related to redefined
|
||||
variables.)
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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: `...` |
|
||||
| W292 | NoNewLineAtEndOfFile | No newline at end of file |
|
||||
|
||||
ruff also implements some of the most popular Flake8 plugins natively, including:
|
||||
### pydocstyle
|
||||
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (3/32)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (8/34)
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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 | NoBlankLineAfterSummary | 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 |
|
||||
|
||||
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
### pyupgrade
|
||||
|
||||
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.
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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)` |
|
||||
|
||||
## Rules
|
||||
### pep8-naming
|
||||
|
||||
The ✅ emoji indicates a rule is enabled by default.
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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 classmethod should be named `cls` |
|
||||
| N805 | InvalidFirstArgumentNameForMethod | first argument of a method should be named `self` |
|
||||
|
||||
The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` command-line option.
|
||||
### flake8-comprehensions
|
||||
|
||||
| Code | Name | Message | | |
|
||||
| ---- | ---- | ------- | --- | --- |
|
||||
| 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: `...` | ✅ | |
|
||||
| W292 | NoNewLineAtEndOfFile | No newline at end of file | ✅ | |
|
||||
| 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/method | ✅ | |
|
||||
| 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` | ✅ | |
|
||||
| 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 | | |
|
||||
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | | 🛠 |
|
||||
| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | | 🛠 |
|
||||
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | | |
|
||||
| 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 | | |
|
||||
| T201 | PrintFound | `print` found | | 🛠 |
|
||||
| T203 | PPrintFound | `pprint` found | | 🛠 |
|
||||
| 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)` | | 🛠 |
|
||||
| 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 | NoBlankLineAfterSummary | 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 | | |
|
||||
| M001 | UnusedNOQA | Unused `noqa` directive | | 🛠 |
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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 |
|
||||
|
||||
## Integrations
|
||||
### flake8-bugbear
|
||||
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` |
|
||||
| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` |
|
||||
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` |
|
||||
|
||||
### flake8-builtins
|
||||
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| 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 |
|
||||
| ---- | ---- | ------- |
|
||||
| T201 | PrintFound | `print` found |
|
||||
| T203 | PPrintFound | `pprint` found |
|
||||
|
||||
### Meta rules
|
||||
|
||||
| Code | Name | Message |
|
||||
| ---- | ---- | ------- |
|
||||
| M001 | UnusedNOQA | Unused `noqa` directive |
|
||||
|
||||
## Editor Integrations
|
||||
|
||||
### PyCharm
|
||||
|
||||
ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
|
||||
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
|
||||
in PyCharm. Open the Preferences pane, then navigate to "Tools", then "External Tools". From there,
|
||||
add a new tool with the following configuration:
|
||||
|
||||

|
||||

|
||||
|
||||
ruff should then appear as a runnable action:
|
||||
Ruff should then appear as a runnable action:
|
||||
|
||||

|
||||

|
||||
|
||||
### GitHub Actions
|
||||
|
||||
GitHub Actions has everything you need to run ruff out-of-the-box:
|
||||
GitHub Actions has everything you need to run Ruff out-of-the-box:
|
||||
|
||||
```yaml
|
||||
name: CI
|
||||
@@ -394,13 +431,175 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install ruff
|
||||
- name: Run ruff
|
||||
- name: Run Ruff
|
||||
run: ruff .
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### Is Ruff compatible with Black?
|
||||
|
||||
Yes. Ruff is compatible with [Black](https://github.com/psf/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:
|
||||
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (3/32)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (8/34)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
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:
|
||||
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (3/32)
|
||||
|
||||
Ruff also implements the functionality that you get from [`yesqa`](https://github.com/asottile/yesqa),
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (8/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`](https://pypi.org/project/ruff/) on PyPI:
|
||||
|
||||
```shell
|
||||
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](https://github.com/charliermarsh/ruff/issues/2830) 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](https://www.pydocstyle.org/en/latest/error_codes.html#default-conventions).
|
||||
|
||||
For example, if you're coming from `flake8-docstrings`, the following configuration is equivalent to
|
||||
`--docstring-convention numpy`:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
extend-select = [
|
||||
"D100",
|
||||
"D101",
|
||||
"D102",
|
||||
"D103",
|
||||
"D104",
|
||||
"D105",
|
||||
"D106",
|
||||
"D200",
|
||||
"D201",
|
||||
"D202",
|
||||
"D204",
|
||||
"D205",
|
||||
"D206",
|
||||
"D207",
|
||||
"D208",
|
||||
"D209",
|
||||
"D210",
|
||||
"D211",
|
||||
"D214",
|
||||
"D215",
|
||||
"D300",
|
||||
"D400",
|
||||
"D402",
|
||||
"D403",
|
||||
"D404",
|
||||
"D405",
|
||||
"D406",
|
||||
"D407",
|
||||
"D408",
|
||||
"D409",
|
||||
"D410",
|
||||
"D411",
|
||||
"D412",
|
||||
"D413",
|
||||
"D418",
|
||||
"D419",
|
||||
]
|
||||
```
|
||||
|
||||
Similarly, the following is equivalent to `--docstring-convention google`:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
extend-select = [
|
||||
"D100",
|
||||
"D101",
|
||||
"D102",
|
||||
"D103",
|
||||
"D104",
|
||||
"D105",
|
||||
"D106",
|
||||
"D107",
|
||||
"D200",
|
||||
"D201",
|
||||
"D202",
|
||||
"D205",
|
||||
"D206",
|
||||
"D207",
|
||||
"D208",
|
||||
"D209",
|
||||
"D210",
|
||||
"D211",
|
||||
"D212",
|
||||
"D214",
|
||||
"D300",
|
||||
"D402",
|
||||
"D403",
|
||||
"D405",
|
||||
"D410",
|
||||
"D411",
|
||||
"D412",
|
||||
"D414",
|
||||
"D415",
|
||||
"D416",
|
||||
"D417",
|
||||
"D418",
|
||||
"D419",
|
||||
]
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
ruff is written in Rust (1.63.0). You'll need to install the [Rust toolchain](https://www.rust-lang.org/tools/install)
|
||||
Ruff is written in Rust (1.63.0). You'll need to install the [Rust toolchain](https://www.rust-lang.org/tools/install)
|
||||
for development.
|
||||
|
||||
Assuming you have `cargo` installed, you can run:
|
||||
@@ -412,13 +611,13 @@ cargo clippy
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Deployment
|
||||
## Releases
|
||||
|
||||
ruff is distributed on [PyPI](https://pypi.org/project/ruff/), and published via [`maturin`](https://github.com/PyO3/maturin).
|
||||
Ruff is distributed on [PyPI](https://pypi.org/project/ruff/), and published via [`maturin`](https://github.com/PyO3/maturin).
|
||||
|
||||
See: `.github/workflows/release.yaml`.
|
||||
|
||||
## Benchmarking
|
||||
## Benchmarks
|
||||
|
||||
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
|
||||
which makes it a good target for benchmarking.
|
||||
@@ -495,9 +694,9 @@ hyperfine --ignore-failure --warmup 5 \
|
||||
|
||||
In order, these evaluate:
|
||||
|
||||
- ruff
|
||||
- Ruff
|
||||
- Pylint
|
||||
- PyFlakes
|
||||
- Pyflakes
|
||||
- autoflake
|
||||
- pycodestyle
|
||||
- Flake8
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
/// Generate a Markdown-compatible table of supported lint rules.
|
||||
//! Generate a Markdown-compatible table of supported lint rules.
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff::checks::{CheckCode, DEFAULT_CHECK_CODES};
|
||||
use ruff::checks::{CheckCategory, CheckCode};
|
||||
|
||||
fn main() {
|
||||
println!("| Code | Name | Message | | |");
|
||||
println!("| ---- | ---- | ------- | --- | --- |");
|
||||
for check_code in CheckCode::iter() {
|
||||
let check_kind = check_code.kind();
|
||||
let default_token = if DEFAULT_CHECK_CODES.contains(&check_code) {
|
||||
"✅"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let fix_token = if check_kind.fixable() { "🛠" } else { "" };
|
||||
println!(
|
||||
"| {} | {} | {} | {} | {} |",
|
||||
check_kind.code().as_ref(),
|
||||
check_kind.as_ref(),
|
||||
check_kind.body().replace("|", r"\|"),
|
||||
default_token,
|
||||
fix_token
|
||||
);
|
||||
for check_category in CheckCategory::iter() {
|
||||
println!("### {}", check_category.title());
|
||||
println!();
|
||||
|
||||
println!("| Code | Name | Message |");
|
||||
println!("| ---- | ---- | ------- |");
|
||||
for check_code in CheckCode::iter() {
|
||||
if check_code.category() == check_category {
|
||||
let check_kind = check_code.kind();
|
||||
println!(
|
||||
"| {} | {} | {} |",
|
||||
check_kind.code().as_ref(),
|
||||
check_kind.as_ref(),
|
||||
check_kind.body().replace("|", r"\|")
|
||||
);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/// Print the AST for a given Python file.
|
||||
//! Print the AST for a given Python file.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/// Print the token stream for a given Python file.
|
||||
//! Print the token stream for a given Python file.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
2
resources/test/fixtures/F401_0.py
vendored
2
resources/test/fixtures/F401_0.py
vendored
@@ -61,7 +61,7 @@ Y = TypeVar("Y", bound="Dict")
|
||||
Z = TypeVar("Z", "List", "Set")
|
||||
|
||||
a = list["Fruit"]
|
||||
b = Union["Nut", None]
|
||||
b = Union["""Nut""", None]
|
||||
c = cast("Vegetable", b)
|
||||
|
||||
Field = lambda default=MISSING: field(default=default)
|
||||
|
||||
34
resources/test/fixtures/N801.py
vendored
Normal file
34
resources/test/fixtures/N801.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
class bad:
|
||||
pass
|
||||
|
||||
|
||||
class _bad:
|
||||
pass
|
||||
|
||||
|
||||
class bad_class:
|
||||
pass
|
||||
|
||||
|
||||
class Bad_Class:
|
||||
pass
|
||||
|
||||
|
||||
class BAD_CLASS:
|
||||
pass
|
||||
|
||||
|
||||
class Good:
|
||||
pass
|
||||
|
||||
|
||||
class _Good:
|
||||
pass
|
||||
|
||||
|
||||
class GoodClass:
|
||||
pass
|
||||
|
||||
|
||||
class GOOD:
|
||||
pass
|
||||
26
resources/test/fixtures/N802.py
vendored
Normal file
26
resources/test/fixtures/N802.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
def Bad():
|
||||
pass
|
||||
|
||||
|
||||
def _Bad():
|
||||
pass
|
||||
|
||||
|
||||
def BAD():
|
||||
pass
|
||||
|
||||
|
||||
def BAD_FUNC():
|
||||
pass
|
||||
|
||||
|
||||
def good():
|
||||
pass
|
||||
|
||||
|
||||
def _good():
|
||||
pass
|
||||
|
||||
|
||||
def good_func():
|
||||
pass
|
||||
7
resources/test/fixtures/N803.py
vendored
Normal file
7
resources/test/fixtures/N803.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
def func(a, A):
|
||||
return a, A
|
||||
|
||||
|
||||
class Class:
|
||||
def method(self, a, A):
|
||||
return a, A
|
||||
19
resources/test/fixtures/N804.py
vendored
Normal file
19
resources/test/fixtures/N804.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
class Class:
|
||||
@classmethod
|
||||
def bad_class_method(this):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def good_class_method(cls):
|
||||
pass
|
||||
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def static_method(x):
|
||||
return x
|
||||
|
||||
|
||||
def func(x):
|
||||
return x
|
||||
26
resources/test/fixtures/N805.py
vendored
Normal file
26
resources/test/fixtures/N805.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import random
|
||||
|
||||
|
||||
class Class:
|
||||
def bad_method(this):
|
||||
pass
|
||||
|
||||
if random.random(0, 2) == 0:
|
||||
|
||||
def extra_bad_method(this):
|
||||
pass
|
||||
|
||||
def good_method(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def class_method(cls):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def static_method(x):
|
||||
return x
|
||||
|
||||
|
||||
def func(x):
|
||||
return x
|
||||
19
scripts/.flake8
Normal file
19
scripts/.flake8
Normal file
@@ -0,0 +1,19 @@
|
||||
[flake8]
|
||||
exclude =
|
||||
# Defaults
|
||||
.svn,
|
||||
CVS,
|
||||
.bzr,
|
||||
.hg,
|
||||
.git,
|
||||
__pycache__,
|
||||
.tox,
|
||||
.idea,
|
||||
.mypy_cache,
|
||||
.venv,
|
||||
node_modules,
|
||||
# Custom
|
||||
_state_machine.py,
|
||||
test_fstring.py,
|
||||
bad_coding2.py,
|
||||
badsyntax_*.py
|
||||
@@ -757,7 +757,7 @@ pub fn unnecessary_generator_set(expr: &Expr, func: &Expr, args: &Vec<Expr>) ->
|
||||
if id == "set" {
|
||||
if let ExprKind::GeneratorExp { .. } = &args[0].node {
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryGeneratorList,
|
||||
CheckKind::UnnecessaryGeneratorSet,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
@@ -776,7 +776,7 @@ pub fn unnecessary_generator_dict(expr: &Expr, func: &Expr, args: &Vec<Expr>) ->
|
||||
match &elt.node {
|
||||
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryListComprehensionDict,
|
||||
CheckKind::UnnecessaryGeneratorDict,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
@@ -1331,3 +1331,99 @@ pub fn print_call(
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// pep8-naming
|
||||
pub fn invalid_class_name(class_def: &Stmt, name: &str) -> Option<Check> {
|
||||
let stripped = name.strip_prefix('_').unwrap_or(name);
|
||||
if !stripped
|
||||
.chars()
|
||||
.next()
|
||||
.map(|c| c.is_uppercase())
|
||||
.unwrap_or(false)
|
||||
|| stripped.contains('_')
|
||||
{
|
||||
return Some(Check::new(
|
||||
CheckKind::InvalidClassName(name.to_string()),
|
||||
Range::from_located(class_def),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn invalid_function_name(func_def: &Stmt, name: &str) -> Option<Check> {
|
||||
if name.chars().any(|c| c.is_uppercase()) {
|
||||
return Some(Check::new(
|
||||
CheckKind::InvalidFunctionName(name.to_string()),
|
||||
Range::from_located(func_def),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn invalid_argument_name(location: Range, name: &str) -> Option<Check> {
|
||||
if name.chars().any(|c| c.is_uppercase()) {
|
||||
return Some(Check::new(
|
||||
CheckKind::InvalidArgumentName(name.to_string()),
|
||||
location,
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn invalid_first_argument_name_for_class_method(
|
||||
scope: &Scope,
|
||||
decorator_list: &[Expr],
|
||||
args: &Arguments,
|
||||
) -> Option<Check> {
|
||||
if !matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
if let ExprKind::Name { id, .. } = &decorator.node {
|
||||
id == "classmethod"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
if let Some(arg) = args.args.first() {
|
||||
if arg.node.arg != "cls" {
|
||||
return Some(Check::new(
|
||||
CheckKind::InvalidFirstArgumentNameForClassMethod,
|
||||
Range::from_located(arg),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn invalid_first_argument_name_for_method(
|
||||
scope: &Scope,
|
||||
decorator_list: &[Expr],
|
||||
args: &Arguments,
|
||||
) -> Option<Check> {
|
||||
if !matches!(scope.kind, ScopeKind::Class) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
if let ExprKind::Name { id, .. } = &decorator.node {
|
||||
id == "classmethod" || id == "staticmethod"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(arg) = args.args.first() {
|
||||
if arg.node.arg != "self" {
|
||||
return Some(Check::new(
|
||||
CheckKind::InvalidFirstArgumentNameForMethod,
|
||||
Range::from_located(arg),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
10
src/cache.rs
10
src/cache.rs
@@ -1,3 +1,9 @@
|
||||
// cacache uses asyncd-std which has no wasm support, so currently no caching support on wasm
|
||||
#![cfg_attr(
|
||||
target_family = "wasm",
|
||||
allow(unused_imports, unused_variables, dead_code)
|
||||
)]
|
||||
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs::{create_dir_all, File, Metadata};
|
||||
use std::hash::{Hash, Hasher};
|
||||
@@ -5,6 +11,7 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use cacache::Error::EntryNotFound;
|
||||
use filetime::FileTime;
|
||||
use log::error;
|
||||
@@ -107,6 +114,7 @@ pub fn get(
|
||||
return None;
|
||||
};
|
||||
|
||||
#[cfg(not(target_family = "wasm"))] // cacache needs async-std which doesn't support wasm
|
||||
match cacache::read_sync(cache_dir(), cache_key(path, settings, autofix)) {
|
||||
Ok(encoded) => match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
Ok(CheckResult {
|
||||
@@ -137,12 +145,14 @@ pub fn set(
|
||||
return;
|
||||
};
|
||||
|
||||
#[cfg(not(target_family = "wasm"))] // modification date not supported on wasm
|
||||
let check_result = CheckResultRef {
|
||||
metadata: &CacheMetadata {
|
||||
mtime: FileTime::from_last_modification_time(metadata).unix_seconds(),
|
||||
},
|
||||
messages,
|
||||
};
|
||||
#[cfg(not(target_family = "wasm"))] // cacache needs async-std which doesn't support wasm
|
||||
if let Err(e) = cacache::write_sync(
|
||||
cache_dir(),
|
||||
cache_key(path, settings, autofix),
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
|
||||
use log::error;
|
||||
use rustpython_ast::Location;
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
|
||||
KeywordData, Operator, Stmt, StmtKind, Suite,
|
||||
@@ -34,7 +33,6 @@ pub const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
pub struct Checker<'a> {
|
||||
// Input data.
|
||||
pub(crate) path: &'a Path,
|
||||
// TODO(charlie): Separate immutable from mutable state. (None of these should ever change.)
|
||||
pub(crate) locator: SourceCodeLocator<'a>,
|
||||
pub(crate) settings: &'a Settings,
|
||||
pub(crate) autofix: &'a fixer::Mode,
|
||||
@@ -216,6 +214,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N802) {
|
||||
if let Some(check) = checkers::invalid_function_name(stmt, name) {
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N804) {
|
||||
if let Some(check) = checkers::invalid_first_argument_name_for_class_method(
|
||||
self.current_scope(),
|
||||
decorator_list,
|
||||
args,
|
||||
) {
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N805) {
|
||||
if let Some(check) = checkers::invalid_first_argument_name_for_method(
|
||||
self.current_scope(),
|
||||
decorator_list,
|
||||
args,
|
||||
) {
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_builtin_shadowing(name, Range::from_located(stmt), true);
|
||||
|
||||
// Visit the decorators and arguments, but avoid the body, which will be deferred.
|
||||
@@ -304,6 +328,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N801) {
|
||||
if let Some(check) = checkers::invalid_class_name(stmt, name) {
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_builtin_shadowing(
|
||||
name,
|
||||
self.locate_check(Range::from_located(stmt)),
|
||||
@@ -1336,6 +1366,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::N803) {
|
||||
if let Some(check) =
|
||||
checkers::invalid_argument_name(Range::from_located(arg), &arg.node.arg)
|
||||
{
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
self.check_builtin_arg_shadowing(&arg.node.arg, Range::from_located(arg));
|
||||
}
|
||||
}
|
||||
@@ -1711,29 +1749,6 @@ impl<'a> Checker<'a> {
|
||||
'b: 'a,
|
||||
{
|
||||
while let Some((range, expression)) = self.deferred_string_annotations.pop() {
|
||||
// HACK(charlie): We need to modify `range` such that it represents the range of the
|
||||
// expression _within_ the string annotation (as opposed to the range of the string
|
||||
// annotation itself). RustPython seems to return an off-by-one start column for every
|
||||
// string value, so we check for double quotes (which are really triple quotes).
|
||||
let contents = self.locator.slice_source_code_at(&range.location);
|
||||
let range = if contents.starts_with("\"\"") || contents.starts_with("\'\'") {
|
||||
Range {
|
||||
location: Location::new(range.location.row(), range.location.column() + 2),
|
||||
end_location: Location::new(
|
||||
range.end_location.row(),
|
||||
range.end_location.column() - 2,
|
||||
),
|
||||
}
|
||||
} else {
|
||||
Range {
|
||||
location: Location::new(range.location.row(), range.location.column()),
|
||||
end_location: Location::new(
|
||||
range.end_location.row(),
|
||||
range.end_location.column() - 1,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(mut expr) = parser::parse_expression(expression, "<filename>") {
|
||||
relocate_expr(&mut expr, range);
|
||||
allocator.push(expr);
|
||||
@@ -2029,10 +2044,8 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn check_builtin_shadowing(&mut self, name: &str, location: Range, is_attribute: bool) {
|
||||
let scope = self.current_scope();
|
||||
|
||||
// flake8-builtins
|
||||
if is_attribute && matches!(scope.kind, ScopeKind::Class) {
|
||||
if is_attribute && matches!(self.current_scope().kind, ScopeKind::Class) {
|
||||
if self.settings.enabled.contains(&CheckCode::A003) {
|
||||
if let Some(check) = checkers::builtin_shadowing(
|
||||
name,
|
||||
@@ -2054,6 +2067,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn check_builtin_arg_shadowing(&mut self, name: &str, location: Range) {
|
||||
// flake8-builtins
|
||||
if self.settings.enabled.contains(&CheckCode::A002) {
|
||||
if let Some(check) = checkers::builtin_shadowing(
|
||||
name,
|
||||
|
||||
205
src/checks.rs
205
src/checks.rs
@@ -200,10 +200,47 @@ pub enum CheckCode {
|
||||
D417,
|
||||
D418,
|
||||
D419,
|
||||
// pep8-naming
|
||||
N801,
|
||||
N802,
|
||||
N803,
|
||||
N804,
|
||||
N805,
|
||||
// Meta
|
||||
M001,
|
||||
}
|
||||
|
||||
#[derive(EnumIter, Debug, PartialEq, Eq)]
|
||||
pub enum CheckCategory {
|
||||
Pyflakes,
|
||||
Pycodestyle,
|
||||
Pydocstyle,
|
||||
Pyupgrade,
|
||||
Pep8Naming,
|
||||
Flake8Comprehensions,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
Flake8Print,
|
||||
Meta,
|
||||
}
|
||||
|
||||
impl CheckCategory {
|
||||
pub fn title(&self) -> &'static str {
|
||||
match self {
|
||||
CheckCategory::Pycodestyle => "pycodestyle",
|
||||
CheckCategory::Pyflakes => "Pyflakes",
|
||||
CheckCategory::Flake8Builtins => "flake8-builtins",
|
||||
CheckCategory::Flake8Bugbear => "flake8-bugbear",
|
||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||
CheckCategory::Flake8Print => "flake8-print",
|
||||
CheckCategory::Pyupgrade => "pyupgrade",
|
||||
CheckCategory::Pydocstyle => "pydocstyle",
|
||||
CheckCategory::Pep8Naming => "pep8-naming",
|
||||
CheckCategory::Meta => "Meta rules",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum LintSource {
|
||||
AST,
|
||||
@@ -346,6 +383,12 @@ pub enum CheckKind {
|
||||
SectionUnderlineNotOverIndented(String),
|
||||
SkipDocstring,
|
||||
UsesTripleQuotes,
|
||||
// pep8-naming
|
||||
InvalidClassName(String),
|
||||
InvalidFunctionName(String),
|
||||
InvalidArgumentName(String),
|
||||
InvalidFirstArgumentNameForClassMethod,
|
||||
InvalidFirstArgumentNameForMethod,
|
||||
// Meta
|
||||
UnusedNOQA(Option<Vec<String>>),
|
||||
}
|
||||
@@ -354,7 +397,7 @@ impl CheckCode {
|
||||
/// The source for the check (either the AST, the filesystem, or the physical lines).
|
||||
pub fn lint_source(&self) -> &'static LintSource {
|
||||
match self {
|
||||
CheckCode::E501 | CheckCode::M001 => &LintSource::Lines,
|
||||
CheckCode::E501 | CheckCode::W292 | CheckCode::M001 => &LintSource::Lines,
|
||||
CheckCode::E902 => &LintSource::FileSystem,
|
||||
_ => &LintSource::AST,
|
||||
}
|
||||
@@ -515,10 +558,146 @@ impl CheckCode {
|
||||
}
|
||||
CheckCode::D418 => CheckKind::SkipDocstring,
|
||||
CheckCode::D419 => CheckKind::NonEmpty,
|
||||
// pep8-naming
|
||||
CheckCode::N801 => CheckKind::InvalidClassName("...".to_string()),
|
||||
CheckCode::N802 => CheckKind::InvalidFunctionName("...".to_string()),
|
||||
CheckCode::N803 => CheckKind::InvalidArgumentName("...".to_string()),
|
||||
CheckCode::N804 => CheckKind::InvalidFirstArgumentNameForClassMethod,
|
||||
CheckCode::N805 => CheckKind::InvalidFirstArgumentNameForMethod,
|
||||
// Meta
|
||||
CheckCode::M001 => CheckKind::UnusedNOQA(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn category(&self) -> CheckCategory {
|
||||
match self {
|
||||
CheckCode::E402 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E501 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E711 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E712 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E713 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E714 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E721 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E722 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E731 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E741 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E742 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E743 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E902 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E999 => CheckCategory::Pycodestyle,
|
||||
CheckCode::W292 => CheckCategory::Pycodestyle,
|
||||
CheckCode::F401 => CheckCategory::Pyflakes,
|
||||
CheckCode::F402 => CheckCategory::Pyflakes,
|
||||
CheckCode::F403 => CheckCategory::Pyflakes,
|
||||
CheckCode::F404 => CheckCategory::Pyflakes,
|
||||
CheckCode::F405 => CheckCategory::Pyflakes,
|
||||
CheckCode::F406 => CheckCategory::Pyflakes,
|
||||
CheckCode::F407 => CheckCategory::Pyflakes,
|
||||
CheckCode::F541 => CheckCategory::Pyflakes,
|
||||
CheckCode::F601 => CheckCategory::Pyflakes,
|
||||
CheckCode::F602 => CheckCategory::Pyflakes,
|
||||
CheckCode::F621 => CheckCategory::Pyflakes,
|
||||
CheckCode::F622 => CheckCategory::Pyflakes,
|
||||
CheckCode::F631 => CheckCategory::Pyflakes,
|
||||
CheckCode::F632 => CheckCategory::Pyflakes,
|
||||
CheckCode::F633 => CheckCategory::Pyflakes,
|
||||
CheckCode::F634 => CheckCategory::Pyflakes,
|
||||
CheckCode::F701 => CheckCategory::Pyflakes,
|
||||
CheckCode::F702 => CheckCategory::Pyflakes,
|
||||
CheckCode::F704 => CheckCategory::Pyflakes,
|
||||
CheckCode::F706 => CheckCategory::Pyflakes,
|
||||
CheckCode::F707 => CheckCategory::Pyflakes,
|
||||
CheckCode::F722 => CheckCategory::Pyflakes,
|
||||
CheckCode::F821 => CheckCategory::Pyflakes,
|
||||
CheckCode::F822 => CheckCategory::Pyflakes,
|
||||
CheckCode::F823 => CheckCategory::Pyflakes,
|
||||
CheckCode::F831 => CheckCategory::Pyflakes,
|
||||
CheckCode::F841 => CheckCategory::Pyflakes,
|
||||
CheckCode::F901 => CheckCategory::Pyflakes,
|
||||
CheckCode::A001 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::A002 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::A003 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::B011 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B014 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B025 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::C400 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C401 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C402 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C403 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C404 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C405 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C406 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C408 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C409 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C410 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C411 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C413 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C414 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C415 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C416 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C417 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::T201 => CheckCategory::Flake8Print,
|
||||
CheckCode::T203 => CheckCategory::Flake8Print,
|
||||
CheckCode::U001 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U002 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U003 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U004 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U005 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U006 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U007 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U008 => CheckCategory::Pyupgrade,
|
||||
CheckCode::D100 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D101 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D102 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D103 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D104 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D105 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D106 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D107 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D200 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D201 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D202 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D203 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D204 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D205 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D206 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D207 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D208 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D209 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D210 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D211 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D212 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D213 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D214 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D215 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D300 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D400 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D402 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D403 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D404 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D405 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D406 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D407 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D408 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D409 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D410 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D411 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D412 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D413 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D414 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D415 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D416 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D417 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D418 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D419 => CheckCategory::Pydocstyle,
|
||||
CheckCode::N801 => CheckCategory::Pep8Naming,
|
||||
CheckCode::N802 => CheckCategory::Pep8Naming,
|
||||
CheckCode::N803 => CheckCategory::Pep8Naming,
|
||||
CheckCode::N804 => CheckCategory::Pep8Naming,
|
||||
CheckCode::N805 => CheckCategory::Pep8Naming,
|
||||
CheckCode::M001 => CheckCategory::Meta,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckKind {
|
||||
@@ -652,6 +831,12 @@ impl CheckKind {
|
||||
CheckKind::SectionUnderlineNotOverIndented(_) => &CheckCode::D215,
|
||||
CheckKind::SkipDocstring => &CheckCode::D418,
|
||||
CheckKind::UsesTripleQuotes => &CheckCode::D300,
|
||||
// pep8-naming
|
||||
CheckKind::InvalidClassName(_) => &CheckCode::N801,
|
||||
CheckKind::InvalidFunctionName(_) => &CheckCode::N802,
|
||||
CheckKind::InvalidArgumentName(_) => &CheckCode::N803,
|
||||
CheckKind::InvalidFirstArgumentNameForClassMethod => &CheckCode::N804,
|
||||
CheckKind::InvalidFirstArgumentNameForMethod => &CheckCode::N805,
|
||||
// Meta
|
||||
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
|
||||
}
|
||||
@@ -966,7 +1151,7 @@ impl CheckKind {
|
||||
CheckKind::PublicPackage => "Missing docstring in public package".to_string(),
|
||||
CheckKind::MagicMethod => "Missing docstring in magic method".to_string(),
|
||||
CheckKind::PublicNestedClass => "Missing docstring in public nested class".to_string(),
|
||||
CheckKind::PublicInit => "Missing docstring in __init__".to_string(),
|
||||
CheckKind::PublicInit => "Missing docstring in `__init__`".to_string(),
|
||||
CheckKind::NoThisPrefix => {
|
||||
"First word of the docstring should not be `This`".to_string()
|
||||
}
|
||||
@@ -1026,6 +1211,22 @@ impl CheckKind {
|
||||
}
|
||||
CheckKind::NoUnderIndentation => "Docstring is under-indented".to_string(),
|
||||
CheckKind::NoOverIndentation => "Docstring is over-indented".to_string(),
|
||||
// pep8-naming
|
||||
CheckKind::InvalidClassName(name) => {
|
||||
format!("class name `{name}` should use CapWords convention ")
|
||||
}
|
||||
CheckKind::InvalidFunctionName(name) => {
|
||||
format!("function name `{name}` should be lowercase")
|
||||
}
|
||||
CheckKind::InvalidArgumentName(name) => {
|
||||
format!("argument name `{name}` should be lowercase")
|
||||
}
|
||||
CheckKind::InvalidFirstArgumentNameForClassMethod => {
|
||||
"first argument of a classmethod should be named `cls`".to_string()
|
||||
}
|
||||
CheckKind::InvalidFirstArgumentNameForMethod => {
|
||||
"first argument of a method should be named `self`".to_string()
|
||||
}
|
||||
// Meta
|
||||
CheckKind::UnusedNOQA(codes) => match codes {
|
||||
None => "Unused `noqa` directive".to_string(),
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::docstrings::google::check_google_section;
|
||||
use crate::docstrings::helpers;
|
||||
use crate::docstrings::helpers::{indentation, leading_space};
|
||||
use crate::docstrings::numpy::check_numpy_section;
|
||||
use crate::docstrings::sections::section_contexts;
|
||||
@@ -138,7 +137,7 @@ pub fn one_liner(checker: &mut Checker, definition: &Definition) {
|
||||
if non_empty_line_count == 1 && line_count > 1 {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::FitsOnOneLine,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -164,7 +163,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
{
|
||||
let (before, _, after) = checker.locator.partition_source_code_at(
|
||||
&Range::from_located(parent),
|
||||
&helpers::range_for(docstring),
|
||||
&Range::from_located(docstring),
|
||||
);
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::D201) {
|
||||
@@ -177,7 +176,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
if blank_lines_before != 0 {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoBlankLineBeforeFunction(blank_lines_before),
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -202,7 +201,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoBlankLineAfterFunction(blank_lines_after),
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -224,7 +223,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
{
|
||||
let (before, _, after) = checker.locator.partition_source_code_at(
|
||||
&Range::from_located(parent),
|
||||
&helpers::range_for(docstring),
|
||||
&Range::from_located(docstring),
|
||||
);
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::D203)
|
||||
@@ -241,7 +240,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoBlankLineBeforeClass(blank_lines_before),
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
if blank_lines_before != 1
|
||||
@@ -249,7 +248,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::OneBlankLineBeforeClass(blank_lines_before),
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -267,7 +266,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
if !all_blank_after && blank_lines_after != 1 {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::OneBlankLineAfterClass(blank_lines_after),
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -297,7 +296,7 @@ pub fn blank_after_summary(checker: &mut Checker, definition: &Definition) {
|
||||
if lines_count > 1 && blanks_count != 1 {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoBlankLineAfterSummary,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -327,7 +326,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D206) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::IndentWithSpaces,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
has_seen_tab = true;
|
||||
@@ -352,7 +351,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D206) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::IndentWithSpaces,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
has_seen_tab = true;
|
||||
@@ -364,7 +363,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D208) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoOverIndentation,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
has_seen_over_indent = true;
|
||||
@@ -376,7 +375,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D207) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoUnderIndentation,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
has_seen_under_indent = true;
|
||||
@@ -403,13 +402,13 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
|
||||
if line_count > 1 {
|
||||
let content = checker
|
||||
.locator
|
||||
.slice_source_code_range(&helpers::range_for(docstring));
|
||||
.slice_source_code_range(&Range::from_located(docstring));
|
||||
if let Some(line) = content.lines().last() {
|
||||
let line = line.trim();
|
||||
if line != "\"\"\"" && line != "'''" {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NewLineAfterLastParagraph,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -436,7 +435,7 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
|
||||
if line.starts_with(' ') || (matches!(lines.next(), None) && line.ends_with(' ')) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoSurroundingWhitespace,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -455,21 +454,29 @@ pub fn multi_line_summary_start(checker: &mut Checker, definition: &Definition)
|
||||
if string.lines().nth(1).is_some() {
|
||||
let content = checker
|
||||
.locator
|
||||
.slice_source_code_range(&helpers::range_for(docstring));
|
||||
.slice_source_code_range(&Range::from_located(docstring));
|
||||
if let Some(first_line) = content.lines().next() {
|
||||
let first_line = first_line.trim();
|
||||
if first_line == "\"\"\"" || first_line == "'''" {
|
||||
let first_line = first_line.trim().to_lowercase();
|
||||
let starts_with_triple = first_line == "\"\"\""
|
||||
|| first_line == "'''"
|
||||
|| first_line == "u\"\"\""
|
||||
|| first_line == "u'''"
|
||||
|| first_line == "r\"\"\""
|
||||
|| first_line == "r'''"
|
||||
|| first_line == "ur\"\"\""
|
||||
|| first_line == "ur'''";
|
||||
if starts_with_triple {
|
||||
if checker.settings.enabled.contains(&CheckCode::D212) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MultiLineSummaryFirstLine,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if checker.settings.enabled.contains(&CheckCode::D213) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MultiLineSummarySecondLine,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -489,19 +496,26 @@ pub fn triple_quotes(checker: &mut Checker, definition: &Definition) {
|
||||
{
|
||||
let content = checker
|
||||
.locator
|
||||
.slice_source_code_range(&helpers::range_for(docstring));
|
||||
if string.contains("\"\"\"") {
|
||||
if !content.starts_with("'''") {
|
||||
.slice_source_code_range(&Range::from_located(docstring));
|
||||
if let Some(first_line) = content.lines().next() {
|
||||
let first_line = first_line.trim().to_lowercase();
|
||||
let starts_with_triple = if string.contains("\"\"\"") {
|
||||
first_line.starts_with("'''")
|
||||
|| first_line.starts_with("u'''")
|
||||
|| first_line.starts_with("r'''")
|
||||
|| first_line.starts_with("ur'''")
|
||||
} else {
|
||||
first_line.starts_with("\"\"\"")
|
||||
|| first_line.starts_with("u\"\"\"")
|
||||
|| first_line.starts_with("r\"\"\"")
|
||||
|| first_line.starts_with("ur\"\"\"")
|
||||
};
|
||||
if !starts_with_triple {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UsesTripleQuotes,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
} else if !content.starts_with("\"\"\"") {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::UsesTripleQuotes,
|
||||
helpers::range_for(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,7 +533,7 @@ pub fn ends_with_period(checker: &mut Checker, definition: &Definition) {
|
||||
if !string.ends_with('.') {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::EndsInPeriod,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -544,7 +558,7 @@ pub fn no_signature(checker: &mut Checker, definition: &Definition) {
|
||||
if first_line.contains(&format!("{name}(")) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoSignature,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -579,7 +593,7 @@ pub fn capitalized(checker: &mut Checker, definition: &Definition) {
|
||||
if !first_char.is_uppercase() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::FirstLineCapitalized,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -609,7 +623,7 @@ pub fn starts_with_this(checker: &mut Checker, definition: &Definition) {
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NoThisPrefix,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -629,7 +643,7 @@ pub fn ends_with_punctuation(checker: &mut Checker, definition: &Definition) {
|
||||
if !(string.ends_with('.') || string.ends_with('!') || string.ends_with('?')) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::EndsInPunctuation,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -666,7 +680,7 @@ pub fn not_empty(checker: &mut Checker, definition: &Definition) -> bool {
|
||||
if checker.settings.enabled.contains(&CheckCode::D419) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NonEmpty,
|
||||
helpers::range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::docstrings::helpers::range_for;
|
||||
use crate::docstrings::sections;
|
||||
use crate::docstrings::sections::SectionContext;
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
@@ -136,7 +136,7 @@ pub(crate) fn check_google_section(
|
||||
.expect("Sections are only available for docstrings.");
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::SectionNameEndsInColon(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,18 +20,9 @@ pub fn leading_space(line: &str) -> String {
|
||||
|
||||
/// Extract the leading indentation from a docstring.
|
||||
pub fn indentation<'a>(checker: &'a mut Checker, docstring: &Expr) -> &'a str {
|
||||
let range = range_for(docstring);
|
||||
let range = Range::from_located(docstring);
|
||||
checker.locator.slice_source_code_range(&Range {
|
||||
location: Location::new(range.location.row(), 1),
|
||||
end_location: Location::new(range.location.row(), range.location.column()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract the source code range for a docstring.
|
||||
pub fn range_for(docstring: &Expr) -> Range {
|
||||
// RustPython currently omits the first quotation mark in a string, so offset the location.
|
||||
Range {
|
||||
location: Location::new(docstring.location.row(), docstring.location.column() - 1),
|
||||
end_location: docstring.end_location,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::docstrings::helpers::range_for;
|
||||
use crate::docstrings::sections::SectionContext;
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::docstrings::types::Definition;
|
||||
@@ -100,7 +100,7 @@ pub(crate) fn check_numpy_section(
|
||||
.expect("Sections are only available for docstrings.");
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NewLineAfterSectionName(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::docstrings::helpers;
|
||||
use crate::docstrings::helpers::range_for;
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::docstrings::types::{Definition, DefinitionKind};
|
||||
use crate::visibility::is_static;
|
||||
@@ -132,13 +131,13 @@ fn check_blanks_and_section_underline(
|
||||
if checker.settings.enabled.contains(&CheckCode::D407) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DashedUnderlineAfterSection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
if checker.settings.enabled.contains(&CheckCode::D414) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NonEmptySection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
return;
|
||||
@@ -153,7 +152,7 @@ fn check_blanks_and_section_underline(
|
||||
if checker.settings.enabled.contains(&CheckCode::D407) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DashedUnderlineAfterSection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
if blank_lines_after_header > 0 {
|
||||
@@ -162,7 +161,7 @@ fn check_blanks_and_section_underline(
|
||||
CheckKind::NoBlankLinesBetweenHeaderAndContent(
|
||||
context.section_name.to_string(),
|
||||
),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -171,7 +170,7 @@ fn check_blanks_and_section_underline(
|
||||
if checker.settings.enabled.contains(&CheckCode::D408) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::SectionUnderlineAfterName(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -188,7 +187,7 @@ fn check_blanks_and_section_underline(
|
||||
CheckKind::SectionUnderlineMatchesSectionLength(
|
||||
context.section_name.to_string(),
|
||||
),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -199,7 +198,7 @@ fn check_blanks_and_section_underline(
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::SectionUnderlineNotOverIndented(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -214,7 +213,7 @@ fn check_blanks_and_section_underline(
|
||||
if checker.settings.enabled.contains(&CheckCode::D414) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NonEmptySection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
@@ -223,7 +222,7 @@ fn check_blanks_and_section_underline(
|
||||
CheckKind::NoBlankLinesBetweenHeaderAndContent(
|
||||
context.section_name.to_string(),
|
||||
),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -232,7 +231,7 @@ fn check_blanks_and_section_underline(
|
||||
if checker.settings.enabled.contains(&CheckCode::D414) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::NonEmptySection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -259,7 +258,7 @@ pub(crate) fn check_common_section(
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::CapitalizeSectionName(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -270,7 +269,7 @@ pub(crate) fn check_common_section(
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::SectionNotOverIndented(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -285,14 +284,14 @@ pub(crate) fn check_common_section(
|
||||
if checker.settings.enabled.contains(&CheckCode::D413) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::BlankLineAfterLastSection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
if checker.settings.enabled.contains(&CheckCode::D410) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::BlankLineAfterSection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -302,7 +301,7 @@ pub(crate) fn check_common_section(
|
||||
if !context.previous_line.is_empty() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::BlankLineBeforeSection(context.section_name.to_string()),
|
||||
range_for(docstring),
|
||||
Range::from_located(docstring),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use log::debug;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::{lexer, parser};
|
||||
@@ -122,6 +123,7 @@ pub fn lint_stdin(
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg_attr(target_family = "wasm", allow(unused_variables))]
|
||||
pub fn lint_path(
|
||||
path: &Path,
|
||||
settings: &Settings,
|
||||
@@ -131,6 +133,7 @@ pub fn lint_path(
|
||||
let metadata = path.metadata()?;
|
||||
|
||||
// Check the cache.
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
if let Some(messages) = cache::get(path, &metadata, settings, autofix, mode) {
|
||||
debug!("Cache hit for: {}", path.to_string_lossy());
|
||||
return Ok(messages);
|
||||
@@ -166,6 +169,7 @@ pub fn lint_path(
|
||||
filename: path.to_string_lossy().to_string(),
|
||||
})
|
||||
.collect();
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
cache::set(path, &metadata, settings, autofix, &messages, mode);
|
||||
|
||||
Ok(messages)
|
||||
@@ -349,6 +353,11 @@ mod tests {
|
||||
#[test_case(CheckCode::F831, Path::new("F831.py"); "F831")]
|
||||
#[test_case(CheckCode::F841, Path::new("F841.py"); "F841")]
|
||||
#[test_case(CheckCode::F901, Path::new("F901.py"); "F901")]
|
||||
#[test_case(CheckCode::N801, Path::new("N801.py"); "N801")]
|
||||
#[test_case(CheckCode::N802, Path::new("N802.py"); "N802")]
|
||||
#[test_case(CheckCode::N803, Path::new("N803.py"); "N803")]
|
||||
#[test_case(CheckCode::N804, Path::new("N804.py"); "N804")]
|
||||
#[test_case(CheckCode::N805, Path::new("N805.py"); "N805")]
|
||||
#[test_case(CheckCode::T201, Path::new("T201.py"); "T201")]
|
||||
#[test_case(CheckCode::T203, Path::new("T203.py"); "T203")]
|
||||
#[test_case(CheckCode::U001, Path::new("U001.py"); "U001")]
|
||||
|
||||
62
src/main.rs
62
src/main.rs
@@ -9,9 +9,11 @@ use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use log::{debug, error};
|
||||
use notify::{raw_watcher, RecursiveMode, Watcher};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use rayon::prelude::*;
|
||||
use walkdir::DirEntry;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use ruff::cache;
|
||||
use ruff::checks::CheckCode;
|
||||
use ruff::checks::CheckKind;
|
||||
@@ -29,9 +31,24 @@ use ruff::settings::RawSettings;
|
||||
use ruff::settings::{FilePattern, PerFileIgnore, Settings};
|
||||
use ruff::tell_user;
|
||||
|
||||
#[cfg(feature = "update-informer")]
|
||||
const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
#[cfg(feature = "update-informer")]
|
||||
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// Shim that calls par_iter except for wasm because there's no wasm support in rayon yet
|
||||
/// (there is a shim to be used for the web, but it requires js cooperation)
|
||||
/// Unfortunately, ParallelIterator does not implement Iterator so the signatures diverge
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
fn par_iter<T: Sync>(iterable: &Vec<T>) -> impl ParallelIterator<Item = &T> {
|
||||
iterable.par_iter()
|
||||
}
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
fn par_iter<T: Sync>(iterable: &Vec<T>) -> impl Iterator<Item = &T> {
|
||||
iterable.iter()
|
||||
}
|
||||
|
||||
#[cfg(feature = "update-informer")]
|
||||
fn check_for_updates() {
|
||||
use update_informer::{registry, Check};
|
||||
@@ -104,8 +121,7 @@ fn run_once(
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
let start = Instant::now();
|
||||
let mut messages: Vec<Message> = paths
|
||||
.par_iter()
|
||||
let mut messages: Vec<Message> = par_iter(&paths)
|
||||
.map(|entry| {
|
||||
match entry {
|
||||
Ok(entry) => {
|
||||
@@ -160,8 +176,7 @@ fn add_noqa(files: &[PathBuf], settings: &Settings) -> Result<usize> {
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
let start = Instant::now();
|
||||
let modifications: usize = paths
|
||||
.par_iter()
|
||||
let modifications: usize = par_iter(&paths)
|
||||
.map(|entry| match entry {
|
||||
Ok(entry) => {
|
||||
let path = entry.path();
|
||||
@@ -190,8 +205,7 @@ fn autoformat(files: &[PathBuf], settings: &Settings) -> Result<usize> {
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
|
||||
let start = Instant::now();
|
||||
let modifications = paths
|
||||
.par_iter()
|
||||
let modifications = par_iter(&paths)
|
||||
.map(|entry| {
|
||||
let path = entry.path();
|
||||
autoformat_path(path)
|
||||
@@ -302,6 +316,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
cache::init()?;
|
||||
|
||||
let mut printer = Printer::new(cli.format, cli.verbose);
|
||||
@@ -367,25 +382,30 @@ fn inner_main() -> Result<ExitCode> {
|
||||
println!("Formatted {modifications} files.");
|
||||
}
|
||||
} else {
|
||||
let (messages, print_messages) = if cli.files == vec![PathBuf::from("-")] {
|
||||
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
|
||||
let path = Path::new(&filename);
|
||||
(
|
||||
run_once_stdin(&settings, path, cli.fix)?,
|
||||
!cli.quiet && !cli.fix,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)?,
|
||||
!cli.quiet,
|
||||
)
|
||||
};
|
||||
if print_messages {
|
||||
let (messages, should_print_messages, should_check_updates) =
|
||||
if cli.files == vec![PathBuf::from("-")] {
|
||||
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
|
||||
let path = Path::new(&filename);
|
||||
(
|
||||
run_once_stdin(&settings, path, cli.fix)?,
|
||||
!cli.quiet && !cli.fix,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
run_once(&cli.files, &settings, !cli.no_cache, cli.fix)?,
|
||||
!cli.quiet,
|
||||
!cli.quiet,
|
||||
)
|
||||
};
|
||||
if should_print_messages {
|
||||
printer.write_once(&messages)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "update-informer")]
|
||||
check_for_updates();
|
||||
if should_check_updates {
|
||||
check_for_updates();
|
||||
}
|
||||
|
||||
if messages.iter().any(|message| !message.fixed) && !cli.exit_zero {
|
||||
return Ok(ExitCode::FAILURE);
|
||||
|
||||
@@ -104,6 +104,7 @@ impl Printer {
|
||||
}
|
||||
|
||||
pub fn clear_screen(&mut self) -> Result<()> {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
clearscreen::clear()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnnecessaryGeneratorList
|
||||
- kind: UnnecessaryGeneratorSet
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnnecessaryListComprehensionDict
|
||||
- kind: UnnecessaryGeneratorDict
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
|
||||
@@ -5,7 +5,7 @@ expression: checks
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 302
|
||||
column: 6
|
||||
column: 5
|
||||
end_location:
|
||||
row: 302
|
||||
column: 20
|
||||
@@ -13,7 +13,7 @@ expression: checks
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 307
|
||||
column: 6
|
||||
column: 5
|
||||
end_location:
|
||||
row: 307
|
||||
column: 20
|
||||
@@ -21,7 +21,7 @@ expression: checks
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 312
|
||||
column: 6
|
||||
column: 5
|
||||
end_location:
|
||||
row: 312
|
||||
column: 16
|
||||
@@ -29,7 +29,7 @@ expression: checks
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 317
|
||||
column: 6
|
||||
column: 5
|
||||
end_location:
|
||||
row: 317
|
||||
column: 16
|
||||
@@ -37,7 +37,7 @@ expression: checks
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 323
|
||||
column: 6
|
||||
column: 5
|
||||
end_location:
|
||||
row: 323
|
||||
column: 17
|
||||
|
||||
@@ -5,7 +5,7 @@ expression: checks
|
||||
- kind: FStringMissingPlaceholders
|
||||
location:
|
||||
row: 4
|
||||
column: 7
|
||||
column: 5
|
||||
end_location:
|
||||
row: 4
|
||||
column: 11
|
||||
@@ -13,7 +13,7 @@ expression: checks
|
||||
- kind: FStringMissingPlaceholders
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
column: 5
|
||||
end_location:
|
||||
row: 5
|
||||
column: 11
|
||||
@@ -21,7 +21,7 @@ expression: checks
|
||||
- kind: FStringMissingPlaceholders
|
||||
location:
|
||||
row: 7
|
||||
column: 7
|
||||
column: 5
|
||||
end_location:
|
||||
row: 7
|
||||
column: 11
|
||||
|
||||
@@ -5,7 +5,7 @@ expression: checks
|
||||
- kind: MultiValueRepeatedKeyLiteral
|
||||
location:
|
||||
row: 3
|
||||
column: 6
|
||||
column: 5
|
||||
end_location:
|
||||
row: 3
|
||||
column: 8
|
||||
@@ -21,7 +21,7 @@ expression: checks
|
||||
- kind: MultiValueRepeatedKeyLiteral
|
||||
location:
|
||||
row: 11
|
||||
column: 7
|
||||
column: 5
|
||||
end_location:
|
||||
row: 11
|
||||
column: 11
|
||||
|
||||
@@ -6,9 +6,9 @@ expression: checks
|
||||
ForwardAnnotationSyntaxError: ///
|
||||
location:
|
||||
row: 9
|
||||
column: 13
|
||||
column: 12
|
||||
end_location:
|
||||
row: 9
|
||||
column: 16
|
||||
column: 17
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@ expression: checks
|
||||
UndefinedName: Bar
|
||||
location:
|
||||
row: 58
|
||||
column: 5
|
||||
column: 4
|
||||
end_location:
|
||||
row: 58
|
||||
column: 8
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: TOMATO
|
||||
@@ -60,7 +60,7 @@ expression: checks
|
||||
UndefinedName: B
|
||||
location:
|
||||
row: 87
|
||||
column: 7
|
||||
column: 5
|
||||
end_location:
|
||||
row: 87
|
||||
column: 11
|
||||
@@ -69,7 +69,7 @@ expression: checks
|
||||
UndefinedName: B
|
||||
location:
|
||||
row: 89
|
||||
column: 7
|
||||
column: 5
|
||||
end_location:
|
||||
row: 89
|
||||
column: 9
|
||||
@@ -78,27 +78,27 @@ expression: checks
|
||||
UndefinedName: PEP593Test123
|
||||
location:
|
||||
row: 114
|
||||
column: 10
|
||||
column: 9
|
||||
end_location:
|
||||
row: 114
|
||||
column: 23
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: foo
|
||||
location:
|
||||
row: 122
|
||||
column: 15
|
||||
column: 14
|
||||
end_location:
|
||||
row: 122
|
||||
column: 18
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: bar
|
||||
location:
|
||||
row: 122
|
||||
column: 22
|
||||
column: 21
|
||||
end_location:
|
||||
row: 122
|
||||
column: 25
|
||||
column: 26
|
||||
fix: ~
|
||||
|
||||
|
||||
50
src/snapshots/ruff__linter__tests__N801_N801.py.snap
Normal file
50
src/snapshots/ruff__linter__tests__N801_N801.py.snap
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
InvalidClassName: bad
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
end_location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidClassName: _bad
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
end_location:
|
||||
row: 9
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidClassName: bad_class
|
||||
location:
|
||||
row: 9
|
||||
column: 1
|
||||
end_location:
|
||||
row: 13
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidClassName: Bad_Class
|
||||
location:
|
||||
row: 13
|
||||
column: 1
|
||||
end_location:
|
||||
row: 17
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidClassName: BAD_CLASS
|
||||
location:
|
||||
row: 17
|
||||
column: 1
|
||||
end_location:
|
||||
row: 21
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
41
src/snapshots/ruff__linter__tests__N802_N802.py.snap
Normal file
41
src/snapshots/ruff__linter__tests__N802_N802.py.snap
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
InvalidFunctionName: Bad
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
end_location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidFunctionName: _Bad
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
end_location:
|
||||
row: 9
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidFunctionName: BAD
|
||||
location:
|
||||
row: 9
|
||||
column: 1
|
||||
end_location:
|
||||
row: 13
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidFunctionName: BAD_FUNC
|
||||
location:
|
||||
row: 13
|
||||
column: 1
|
||||
end_location:
|
||||
row: 17
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
23
src/snapshots/ruff__linter__tests__N803_N803.py.snap
Normal file
23
src/snapshots/ruff__linter__tests__N803_N803.py.snap
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
InvalidArgumentName: A
|
||||
location:
|
||||
row: 1
|
||||
column: 13
|
||||
end_location:
|
||||
row: 1
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
InvalidArgumentName: A
|
||||
location:
|
||||
row: 6
|
||||
column: 25
|
||||
end_location:
|
||||
row: 6
|
||||
column: 26
|
||||
fix: ~
|
||||
|
||||
13
src/snapshots/ruff__linter__tests__N804_N804.py.snap
Normal file
13
src/snapshots/ruff__linter__tests__N804_N804.py.snap
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: InvalidFirstArgumentNameForClassMethod
|
||||
location:
|
||||
row: 3
|
||||
column: 26
|
||||
end_location:
|
||||
row: 3
|
||||
column: 30
|
||||
fix: ~
|
||||
|
||||
21
src/snapshots/ruff__linter__tests__N805_N805.py.snap
Normal file
21
src/snapshots/ruff__linter__tests__N805_N805.py.snap
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: InvalidFirstArgumentNameForMethod
|
||||
location:
|
||||
row: 5
|
||||
column: 20
|
||||
end_location:
|
||||
row: 5
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: InvalidFirstArgumentNameForMethod
|
||||
location:
|
||||
row: 10
|
||||
column: 30
|
||||
end_location:
|
||||
row: 10
|
||||
column: 34
|
||||
fix: ~
|
||||
|
||||
@@ -85,49 +85,49 @@ expression: checks
|
||||
- kind: UsePEP604Annotation
|
||||
location:
|
||||
row: 32
|
||||
column: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 32
|
||||
column: 47
|
||||
column: 48
|
||||
fix:
|
||||
content: "str | int | Union[float, bytes]"
|
||||
location:
|
||||
row: 32
|
||||
column: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 32
|
||||
column: 47
|
||||
column: 48
|
||||
applied: false
|
||||
- kind: UsePEP604Annotation
|
||||
location:
|
||||
row: 32
|
||||
column: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 32
|
||||
column: 47
|
||||
column: 48
|
||||
fix:
|
||||
content: float | bytes
|
||||
location:
|
||||
row: 32
|
||||
column: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 32
|
||||
column: 47
|
||||
column: 48
|
||||
applied: false
|
||||
- kind: UsePEP604Annotation
|
||||
location:
|
||||
row: 39
|
||||
column: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 39
|
||||
column: 33
|
||||
column: 34
|
||||
fix:
|
||||
content: str | int
|
||||
location:
|
||||
row: 39
|
||||
column: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 39
|
||||
column: 33
|
||||
column: 34
|
||||
applied: false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user