Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
605416922d | ||
|
|
7b81f36e54 | ||
|
|
ff63da9f52 | ||
|
|
d645a19e0a | ||
|
|
30ae0d3723 | ||
|
|
3fb9e76012 | ||
|
|
0f283ae98c | ||
|
|
269926cec4 | ||
|
|
28018442f6 | ||
|
|
abc9810e2b | ||
|
|
a20482961b | ||
|
|
d97c07818e | ||
|
|
7e92485f43 | ||
|
|
930c3be69d | ||
|
|
24d0a980c5 | ||
|
|
f5f0ed280a | ||
|
|
ca58c72fc9 | ||
|
|
c40f14620a | ||
|
|
04300ce258 | ||
|
|
ead5f948d3 | ||
|
|
e93e9fae82 | ||
|
|
f5ddec0fb3 | ||
|
|
3de2a57416 | ||
|
|
b29b4084ff | ||
|
|
c61ca4a953 | ||
|
|
58d5ac08a8 | ||
|
|
cc63a4be6a | ||
|
|
549a5d44bc | ||
|
|
d65ce6308b | ||
|
|
b988a268e4 | ||
|
|
1c3265ef98 | ||
|
|
8001a1639c | ||
|
|
7d9c1d7a5a | ||
|
|
c5cebb106e | ||
|
|
8c61e8a1ef | ||
|
|
4f338273a5 | ||
|
|
648191652d | ||
|
|
90558609c3 | ||
|
|
991d3c1ef6 | ||
|
|
f472fbc6d4 | ||
|
|
09b65a6449 | ||
|
|
9d2eced941 | ||
|
|
be0f6acb40 | ||
|
|
0c624af036 | ||
|
|
4ca328f964 | ||
|
|
07b5bf7030 | ||
|
|
f40ae943a7 | ||
|
|
8d46d3bfa6 | ||
|
|
4fb0c6e3ad | ||
|
|
ebfdefd110 | ||
|
|
11f06055a0 | ||
|
|
6a6a792562 | ||
|
|
23b622943e | ||
|
|
a7ce8621a9 | ||
|
|
36fb8f7a63 | ||
|
|
e11cf1bf65 | ||
|
|
75e16c0ce5 | ||
|
|
1beedf20f9 | ||
|
|
4758ee6ac4 | ||
|
|
c3dd1b0e3c | ||
|
|
87443e6301 | ||
|
|
16d2ceba79 | ||
|
|
aedee7294e | ||
|
|
4f12b31dc8 | ||
|
|
9f14e7c830 | ||
|
|
4cc492a17a | ||
|
|
028436af81 | ||
|
|
da4994aa73 | ||
|
|
4dcb491bec | ||
|
|
6fc6bf0648 | ||
|
|
c1cb4796f8 | ||
|
|
d81620397e | ||
|
|
84b1490d03 | ||
|
|
28f05aa6e7 | ||
|
|
325faa8e18 | ||
|
|
6bfa1804de | ||
|
|
4dcf284a04 | ||
|
|
08fc9b8095 | ||
|
|
39aed6f11d | ||
|
|
5726118cfe | ||
|
|
67de8ac85e | ||
|
|
b1bda0de82 | ||
|
|
84300e00ff | ||
|
|
fbee95a668 | ||
|
|
afcf5c0ee0 | ||
|
|
0c30768288 | ||
|
|
80295f335b | ||
|
|
465943adf7 | ||
|
|
38eed292e4 | ||
|
|
883e650a35 | ||
|
|
eb1b5e5454 | ||
|
|
8e558a3458 | ||
|
|
4e4643aa5d | ||
|
|
b19258a243 | ||
|
|
7fc42f8f85 | ||
|
|
babe1eb7be | ||
|
|
608b2191aa | ||
|
|
3939c2dbf7 | ||
|
|
20a9252e92 | ||
|
|
a0e3347e43 | ||
|
|
9e704a7c63 | ||
|
|
c9da98e0b7 | ||
|
|
5377d24507 | ||
|
|
db8e4500ee | ||
|
|
bd2de5624e | ||
|
|
3a81f893cc | ||
|
|
fd6dc2a343 | ||
|
|
8693236f9e | ||
|
|
44e2b6208a | ||
|
|
16c81f75c2 | ||
|
|
e1d6ac3265 | ||
|
|
3aec1100f5 | ||
|
|
c00df647e1 | ||
|
|
f012877be1 | ||
|
|
ff6defc988 | ||
|
|
67ca50e9f2 | ||
|
|
6cc160bc2b | ||
|
|
4bdf506d80 | ||
|
|
4af2353ef9 | ||
|
|
6072edf5bf | ||
|
|
4061eeeb32 | ||
|
|
bea6deb0c3 | ||
|
|
81db00a3c4 |
21
.github/workflows/ci.yaml
vendored
21
.github/workflows/ci.yaml
vendored
@@ -70,7 +70,7 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
toolchain: 1.65.0
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo install cargo-insta
|
||||
@@ -84,6 +84,23 @@ jobs:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
- run: RUSTDOCFLAGS="-D warnings" cargo doc --all --no-deps
|
||||
|
||||
scripts:
|
||||
name: "test scripts"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: |
|
||||
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
|
||||
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
|
||||
- run: cargo check
|
||||
|
||||
# TODO(charlie): Re-enable the `wasm-pack` tests.
|
||||
# See: https://github.com/charliermarsh/ruff/issues/1425
|
||||
# wasm-pack-test:
|
||||
@@ -122,7 +139,7 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
toolchain: 1.65.0
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions/setup-python@v4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.227
|
||||
rev: v0.0.233
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
@@ -14,9 +14,10 @@ If you're looking for a place to start, we recommend implementing a new lint rul
|
||||
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
|
||||
existing Python plugins, which can be used as a reference implementation.
|
||||
|
||||
As a concrete example: consider taking on one of the rules in [`flake8-simplify`](https://github.com/charliermarsh/ruff/issues/998),
|
||||
and looking to the originating [Python source](https://github.com/MartinThoma/flake8-simplify) for
|
||||
guidance.
|
||||
As a concrete example: consider taking on one of the rules from the [`tryceratops`](https://github.com/charliermarsh/ruff/issues/2056)
|
||||
plugin, and looking to the originating [Python source](https://github.com/guilatrova/tryceratops)
|
||||
for guidance. [`flake8-simplify`](https://github.com/charliermarsh/ruff/issues/998) has a few rules
|
||||
left too.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -37,13 +38,16 @@ After cloning the repository, run Ruff locally with:
|
||||
cargo run resources/test/fixtures --no-cache
|
||||
```
|
||||
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted, and that it passes
|
||||
both the lint and test validation checks:
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted,
|
||||
and that it passes both the lint and test validation checks.
|
||||
|
||||
For rustfmt and Clippy, we use [nightly Rust][nightly], as it is stricter than stable Rust.
|
||||
(However, tests and builds use stable Rust.)
|
||||
|
||||
```shell
|
||||
cargo +nightly fmt --all # Auto-formatting...
|
||||
cargo +nightly clippy --all # Linting...
|
||||
cargo +nightly test --all # Testing...
|
||||
cargo +nightly clippy --fix --workspace --all-targets --all-features -- -W clippy::pedantic # Linting...
|
||||
cargo test --all # Testing...
|
||||
```
|
||||
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
@@ -66,7 +70,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
6. Update the generated files (documentation and generated code).
|
||||
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
rule origin (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
rule linter (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
contain a struct defined via `define_violation!`, along with a function that creates the violation
|
||||
based on any required inputs. (Many of the existing examples live in `src/violations.rs`, but we're
|
||||
looking to place new rules in their own files.)
|
||||
@@ -78,7 +82,7 @@ collecting diagnostics as it goes.
|
||||
If you need to inspect the AST, you can run `cargo +nightly dev print-ast` with a Python file. Grep
|
||||
for the `Check::new` invocations to understand how other, similar rules are implemented.
|
||||
|
||||
To add a test fixture, create a file under `resources/test/fixtures/[origin]`, named to match
|
||||
To add a test fixture, create a file under `resources/test/fixtures/[linter]`, named to match
|
||||
the code you defined earlier (e.g., `resources/test/fixtures/pycodestyle/E402.py`). This file should
|
||||
contain a variety of violations and non-violations designed to evaluate and demonstrate the behavior
|
||||
of your lint rule.
|
||||
@@ -87,7 +91,7 @@ Run `cargo +nightly dev generate-all` to generate the code for your new fixture.
|
||||
locally with (e.g.) `cargo run resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
|
||||
|
||||
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
|
||||
`test_case` macro in the relevant `src/[origin]/mod.rs` file. Then, run `cargo test --all`.
|
||||
`test_case` macro in the relevant `src/[linter]/mod.rs` file. Then, run `cargo test --all`.
|
||||
Your test will fail, but you'll be prompted to follow-up with `cargo insta review`. Accept the
|
||||
generated snapshot, then commit the snapshot file alongside the rest of your changes.
|
||||
|
||||
@@ -127,3 +131,5 @@ them to [PyPI](https://pypi.org/project/ruff/).
|
||||
|
||||
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
|
||||
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
|
||||
|
||||
[nightly]: https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust
|
||||
|
||||
311
Cargo.lock
generated
311
Cargo.lock
generated
@@ -14,7 +14,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -197,12 +197,6 @@ version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -413,7 +407,7 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -439,7 +433,7 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -484,7 +478,7 @@ version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
@@ -494,7 +488,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
@@ -506,7 +500,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
@@ -518,7 +512,7 @@ version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -592,16 +586,6 @@ dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
@@ -617,7 +601,7 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
@@ -721,7 +705,7 @@ version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys",
|
||||
@@ -735,7 +719,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
@@ -749,7 +733,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"toml_edit",
|
||||
"toml 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -786,24 +770,13 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
@@ -926,6 +899,16 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imperative"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f92123bf2fe0d9f1b5df1964727b970ca3b2d0203d47cf97fb1f36d856b6398"
|
||||
dependencies = [
|
||||
"phf 0.11.1",
|
||||
"rust-stemmers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
@@ -976,7 +959,7 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1001,6 +984,15 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_executable"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@@ -1186,7 +1178,7 @@ version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1265,7 +1257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"static_assertions",
|
||||
]
|
||||
@@ -1419,7 +1411,7 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
@@ -1493,15 +1485,6 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.1"
|
||||
@@ -1512,13 +1495,12 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.8.0"
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_generator 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1532,13 +1514,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
name = "phf_codegen"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
|
||||
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
"rand 0.7.3",
|
||||
"phf_generator 0.11.1",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1548,16 +1530,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
"phf_shared 0.11.1",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1569,6 +1552,15 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
@@ -1650,7 +1642,7 @@ checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1724,20 +1716,6 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -1745,18 +1723,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1766,16 +1734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1784,25 +1743,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1842,7 +1783,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -1894,36 +1835,28 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ropey"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd22239fafefc42138ca5da064f3c17726a80d2379d817a3521240e78dd0064"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
"str_indices",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"clap 4.0.32",
|
||||
"colored",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"criterion",
|
||||
"dirs 4.0.0",
|
||||
"dirs",
|
||||
"fern",
|
||||
"getrandom 0.2.8",
|
||||
"getrandom",
|
||||
"glob",
|
||||
"globset",
|
||||
"ignore",
|
||||
"imperative",
|
||||
"insta",
|
||||
"is_executable",
|
||||
"itertools",
|
||||
"js-sys",
|
||||
"libcst",
|
||||
@@ -1935,7 +1868,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
"path-absolutize",
|
||||
"regex",
|
||||
"ropey",
|
||||
"ruff_macros",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
@@ -1953,14 +1885,14 @@ dependencies = [
|
||||
"textwrap",
|
||||
"thiserror",
|
||||
"titlecase",
|
||||
"toml_edit",
|
||||
"toml 0.6.0",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1997,7 +1929,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
@@ -2018,7 +1950,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
@@ -2027,6 +1959,16 @@ dependencies = [
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-stemmers"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -2062,7 +2004,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2072,11 +2014,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"hexf-parse",
|
||||
"itertools",
|
||||
"lexical-parse-float",
|
||||
@@ -2087,7 +2029,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"radium",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
"siphasher",
|
||||
"unic-ucd-category",
|
||||
"volatile",
|
||||
@@ -2097,7 +2039,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2114,7 +2056,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=4f38cb68e4a97aeea9eb19673803a0bd5f655383#4f38cb68e4a97aeea9eb19673803a0bd5f655383"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2268,13 +2210,22 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c68e921cef53841b8925c2abadd27c9b891d9613bdc43d6b823062866df38e8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd1c7ddea665294d484c39fd0c0d2b7e35bbfe10035c5fe1854741a57f6880e1"
|
||||
dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"dirs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2313,12 +2264,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "str_indices"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.4"
|
||||
@@ -2377,7 +2322,7 @@ version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
@@ -2417,15 +2362,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.7.3"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e"
|
||||
checksum = "da31aef70da0f6352dbcb462683eb4dd2bfad01cf3fc96cf204547b9a839a585"
|
||||
dependencies = [
|
||||
"dirs 2.0.2",
|
||||
"dirs",
|
||||
"fnv",
|
||||
"nom",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen 0.8.0",
|
||||
"phf 0.11.1",
|
||||
"phf_codegen 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2449,7 +2394,7 @@ version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2554,32 +2499,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.0"
|
||||
name = "toml"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd"
|
||||
checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.17.1"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34cc558345efd7e88b9eda9626df2138b80bb46a7606f695e751c892bc7dac6"
|
||||
checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"nom8",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
]
|
||||
|
||||
@@ -2589,7 +2546,7 @@ version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
@@ -2793,12 +2750,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
@@ -2817,7 +2768,7 @@ version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
@@ -2842,7 +2793,7 @@ version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
||||
23
Cargo.toml
23
Cargo.toml
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -35,6 +35,7 @@ fern = { version = "0.6.1" }
|
||||
glob = { version = "0.3.0" }
|
||||
globset = { version = "0.4.9" }
|
||||
ignore = { version = "0.4.18" }
|
||||
imperative = { version = "1.0.3" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
log = { version = "0.4.17" }
|
||||
@@ -45,12 +46,11 @@ num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
ruff_macros = { version = "0.0.227", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.233", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
schemars = { version = "0.8.11" }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
@@ -59,9 +59,9 @@ smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml_edit = { version = "0.17.1", features = ["easy"] }
|
||||
thiserror = { version = "1.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml = { version = "0.6.0" }
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
# For (future) wasm-pack support
|
||||
@@ -73,6 +73,9 @@ serde-wasm-bindgen = { version = "0.4" }
|
||||
js-sys = { version = "0.3.60" }
|
||||
wasm-bindgen = { version = "0.2.83" }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
is_executable = "1.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.19.1", features = ["yaml"] }
|
||||
test-case = { version = "2.2.2" }
|
||||
@@ -97,7 +100,3 @@ opt-level = 3
|
||||
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
|
||||
[profile.dev.package.rustpython-parser]
|
||||
opt-level = 1
|
||||
|
||||
[[bench]]
|
||||
name = "source_code_locator"
|
||||
harness = false
|
||||
|
||||
261
README.md
261
README.md
@@ -51,18 +51,22 @@ Ruff is extremely actively developed and used in major open-source projects like
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Zulip](https://github.com/zulip/zulip)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [Synapse (Matrix)](https://github.com/matrix-org/synapse)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [Polars](https://github.com/pola-rs/polars)
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [Synapse (Matrix)](https://github.com/matrix-org/synapse)
|
||||
- [SnowCLI (Snowflake)](https://github.com/Snowflake-Labs/snowcli)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
|
||||
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
|
||||
@@ -134,19 +138,22 @@ developer of [Zulip](https://github.com/zulip/zulip):
|
||||
1. [eradicate (ERA)](#eradicate-era)
|
||||
1. [pandas-vet (PD)](#pandas-vet-pd)
|
||||
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
||||
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
|
||||
1. [Pylint (PL)](#pylint-pl)
|
||||
1. [flake8-pie (PIE)](#flake8-pie-pie)
|
||||
1. [flake8-commas (COM)](#flake8-commas-com)
|
||||
1. [flake8-no-pep420 (INP)](#flake8-no-pep420-inp)
|
||||
1. [flake8-executable (EXE)](#flake8-executable-exe)
|
||||
1. [flake8-type-checking (TYP)](#flake8-type-checking-typ)
|
||||
1. [tryceratops (TRY)](#tryceratops-try)
|
||||
1. [flake8-use-pathlib (PTH)](#flake8-use-pathlib-pth)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
|
||||
1. [Editor Integrations](#editor-integrations)
|
||||
1. [FAQ](#faq)
|
||||
1. [Development](#development)
|
||||
1. [Contributing](#contributing)
|
||||
1. [Releases](#releases)
|
||||
1. [Benchmarks](#benchmarks)
|
||||
1. [Reference](#reference)
|
||||
1. [License](#license)
|
||||
1. [Contributing](#contributing)
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
@@ -176,6 +183,12 @@ For **Arch Linux** users, Ruff is also available as [`ruff`](https://archlinux.o
|
||||
pacman -S ruff
|
||||
```
|
||||
|
||||
For **Alpine** users, Ruff is also available as [`ruff`](https://pkgs.alpinelinux.org/package/edge/testing/x86_64/ruff) on the testing repositories:
|
||||
|
||||
```shell
|
||||
apk add ruff
|
||||
```
|
||||
|
||||
[](https://repology.org/project/ruff-python-linter/versions)
|
||||
|
||||
### Usage
|
||||
@@ -199,11 +212,9 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.227'
|
||||
rev: 'v0.0.233'
|
||||
hooks:
|
||||
- id: ruff
|
||||
# Respect `exclude` and `extend-exclude` settings.
|
||||
args: ["--force-exclude"]
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -544,7 +555,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||
<!-- Begin auto-generated sections. -->
|
||||
### Pyflakes (F)
|
||||
|
||||
For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
For more, see [Pyflakes](https://pypi.org/project/pyflakes/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -594,11 +605,12 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
|
||||
### pycodestyle (E, W)
|
||||
|
||||
For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI.
|
||||
For more, see [pycodestyle](https://pypi.org/project/pycodestyle/) on PyPI.
|
||||
|
||||
#### Error (E)
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| E101 | mixed-spaces-and-tabs | Indentation contains mixed spaces and tabs | |
|
||||
| E401 | multiple-imports-on-one-line | Multiple imports on one line | |
|
||||
| E402 | module-import-not-at-top-of-file | Module level import not at top of file | |
|
||||
| E501 | line-too-long | Line too long ({length} > {limit} characters) | |
|
||||
@@ -624,7 +636,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||
|
||||
### mccabe (C90)
|
||||
|
||||
For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
For more, see [mccabe](https://pypi.org/project/mccabe/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -632,7 +644,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
|
||||
### isort (I)
|
||||
|
||||
For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
|
||||
For more, see [isort](https://pypi.org/project/isort/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -641,7 +653,7 @@ For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
|
||||
|
||||
### pydocstyle (D)
|
||||
|
||||
For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
For more, see [pydocstyle](https://pypi.org/project/pydocstyle/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -665,13 +677,14 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
| D209 | new-line-after-last-paragraph | Multi-line docstring closing quotes should be on a separate line | 🛠 |
|
||||
| D210 | no-surrounding-whitespace | No whitespaces allowed surrounding docstring text | 🛠 |
|
||||
| D211 | no-blank-line-before-class | No blank lines allowed before class docstring | 🛠 |
|
||||
| D212 | multi-line-summary-first-line | Multi-line docstring summary should start at the first line | |
|
||||
| D213 | multi-line-summary-second-line | Multi-line docstring summary should start at the second line | |
|
||||
| D212 | multi-line-summary-first-line | Multi-line docstring summary should start at the first line | 🛠 |
|
||||
| D213 | multi-line-summary-second-line | Multi-line docstring summary should start at the second line | 🛠 |
|
||||
| D214 | section-not-over-indented | Section is over-indented ("{name}") | 🛠 |
|
||||
| D215 | section-underline-not-over-indented | Section underline is over-indented ("{name}") | 🛠 |
|
||||
| D300 | uses-triple-quotes | Use """triple double quotes""" | |
|
||||
| D301 | uses-r-prefix-for-backslashed-content | Use r""" if any backslashes in a docstring | |
|
||||
| D400 | ends-in-period | First line should end with a period | 🛠 |
|
||||
| D401 | non-imperative-mood | First line of docstring should be in imperative mood: "{first_line}" | |
|
||||
| D402 | no-signature | First line should not be the function's signature | |
|
||||
| D403 | first-line-capitalized | First word of the first line should be properly capitalized | |
|
||||
| D404 | no-this-prefix | First word of the docstring should not be "This" | |
|
||||
@@ -693,7 +706,7 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
|
||||
### pyupgrade (UP)
|
||||
|
||||
For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
For more, see [pyupgrade](https://pypi.org/project/pyupgrade/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -726,12 +739,14 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| UP028 | rewrite-yield-from | Replace `yield` over `for` loop with `yield from` | 🛠 |
|
||||
| UP029 | unnecessary-builtin-import | Unnecessary builtin import: `{import}` | 🛠 |
|
||||
| UP030 | format-literals | Use implicit references for positional format fields | 🛠 |
|
||||
| UP031 | printf-string-formatting | Use format specifiers instead of percent format | 🛠 |
|
||||
| UP032 | f-string | Use f-string instead of `format` call | 🛠 |
|
||||
| UP033 | functools-cache | Use `@functools.cache` instead of `@functools.lru_cache(maxsize=None)` | 🛠 |
|
||||
| UP034 | extraneous-parentheses | Avoid extraneous parentheses | 🛠 |
|
||||
|
||||
### pep8-naming (N)
|
||||
|
||||
For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyPI.
|
||||
For more, see [pep8-naming](https://pypi.org/project/pep8-naming/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -753,7 +768,7 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
|
||||
|
||||
### flake8-2020 (YTT)
|
||||
|
||||
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI.
|
||||
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -770,7 +785,7 @@ For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI
|
||||
|
||||
### flake8-annotations (ANN)
|
||||
|
||||
For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2.9.1/) on PyPI.
|
||||
For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -788,7 +803,7 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2
|
||||
|
||||
### flake8-bandit (S)
|
||||
|
||||
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on PyPI.
|
||||
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -806,11 +821,12 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on
|
||||
| S506 | unsafe-yaml-load | Probable use of unsafe loader `{name}` with `yaml.load`. Allows instantiation of arbitrary objects. Consider `yaml.safe_load`. | |
|
||||
| S508 | snmp-insecure-version | The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. | |
|
||||
| S509 | snmp-weak-cryptography | You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. | |
|
||||
| S612 | logging-config-insecure-listen | Use of insecure `logging.config.listen` detected | |
|
||||
| S701 | jinja2-autoescape-false | Using jinja2 templates with `autoescape=False` is dangerous and can lead to XSS. Ensure `autoescape=True` or use the `select_autoescape` function. | |
|
||||
|
||||
### flake8-blind-except (BLE)
|
||||
|
||||
For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except/0.2.1/) on PyPI.
|
||||
For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -818,7 +834,7 @@ For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except
|
||||
|
||||
### flake8-boolean-trap (FBT)
|
||||
|
||||
For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/0.1.0/) on PyPI.
|
||||
For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -828,7 +844,7 @@ For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap
|
||||
|
||||
### flake8-bugbear (B)
|
||||
|
||||
For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/) on PyPI.
|
||||
For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -863,7 +879,7 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
|
||||
### flake8-builtins (A)
|
||||
|
||||
For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/) on PyPI.
|
||||
For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -873,7 +889,7 @@ For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/)
|
||||
|
||||
### flake8-comprehensions (C4)
|
||||
|
||||
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/3.10.1/) on PyPI.
|
||||
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -896,7 +912,7 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
|
||||
### flake8-debugger (T10)
|
||||
|
||||
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI.
|
||||
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -904,7 +920,7 @@ For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/)
|
||||
|
||||
### flake8-errmsg (EM)
|
||||
|
||||
For more, see [flake8-errmsg](https://pypi.org/project/flake8-errmsg/0.4.0/) on PyPI.
|
||||
For more, see [flake8-errmsg](https://pypi.org/project/flake8-errmsg/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -914,7 +930,7 @@ For more, see [flake8-errmsg](https://pypi.org/project/flake8-errmsg/0.4.0/) on
|
||||
|
||||
### flake8-implicit-str-concat (ISC)
|
||||
|
||||
For more, see [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/0.3.0/) on PyPI.
|
||||
For more, see [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -932,7 +948,7 @@ For more, see [flake8-import-conventions](https://github.com/joaopalmeiro/flake8
|
||||
|
||||
### flake8-print (T20)
|
||||
|
||||
For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on PyPI.
|
||||
For more, see [flake8-print](https://pypi.org/project/flake8-print/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -941,7 +957,7 @@ For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on Py
|
||||
|
||||
### flake8-pytest-style (PT)
|
||||
|
||||
For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/1.6.0/) on PyPI.
|
||||
For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -973,7 +989,7 @@ For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style
|
||||
|
||||
### flake8-quotes (Q)
|
||||
|
||||
For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on PyPI.
|
||||
For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -984,7 +1000,7 @@ For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on
|
||||
|
||||
### flake8-return (RET)
|
||||
|
||||
For more, see [flake8-return](https://pypi.org/project/flake8-return/1.2.0/) on PyPI.
|
||||
For more, see [flake8-return](https://pypi.org/project/flake8-return/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -999,7 +1015,7 @@ For more, see [flake8-return](https://pypi.org/project/flake8-return/1.2.0/) on
|
||||
|
||||
### flake8-simplify (SIM)
|
||||
|
||||
For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/0.19.3/) on PyPI.
|
||||
For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1031,7 +1047,7 @@ For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/0.19.3/
|
||||
|
||||
### flake8-tidy-imports (TID)
|
||||
|
||||
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/4.8.0/) on PyPI.
|
||||
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1040,7 +1056,7 @@ For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports
|
||||
|
||||
### flake8-unused-arguments (ARG)
|
||||
|
||||
For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/0.0.12/) on PyPI.
|
||||
For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1052,7 +1068,7 @@ For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-a
|
||||
|
||||
### flake8-datetimez (DTZ)
|
||||
|
||||
For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/20.10.0/) on PyPI.
|
||||
For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1062,13 +1078,13 @@ For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/20.10
|
||||
| DTZ004 | call-datetime-utcfromtimestamp | The use of `datetime.datetime.utcfromtimestamp()` is not allowed | |
|
||||
| DTZ005 | call-datetime-now-without-tzinfo | The use of `datetime.datetime.now()` without `tz` argument is not allowed | |
|
||||
| DTZ006 | call-datetime-fromtimestamp | The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed | |
|
||||
| DTZ007 | call-datetime-strptime-without-zone | The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` | |
|
||||
| DTZ007 | call-datetime-strptime-without-zone | The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` | |
|
||||
| DTZ011 | call-date-today | The use of `datetime.date.today()` is not allowed. | |
|
||||
| DTZ012 | call-date-fromtimestamp | The use of `datetime.date.fromtimestamp()` is not allowed | |
|
||||
|
||||
### eradicate (ERA)
|
||||
|
||||
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||
For more, see [eradicate](https://pypi.org/project/eradicate/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1076,7 +1092,7 @@ For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||
|
||||
### pandas-vet (PD)
|
||||
|
||||
For more, see [pandas-vet](https://pypi.org/project/pandas-vet/0.2.3/) on PyPI.
|
||||
For more, see [pandas-vet](https://pypi.org/project/pandas-vet/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1104,9 +1120,9 @@ For more, see [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) on GitH
|
||||
| PGH003 | blanket-type-ignore | Use specific rule codes when ignoring type issues | |
|
||||
| PGH004 | blanket-noqa | Use specific rule codes when using `noqa` | |
|
||||
|
||||
### Pylint (PLC, PLE, PLR, PLW)
|
||||
### Pylint (PL)
|
||||
|
||||
For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.
|
||||
|
||||
#### Convention (PLC)
|
||||
| Code | Name | Message | Fix |
|
||||
@@ -1139,18 +1155,20 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
|
||||
### flake8-pie (PIE)
|
||||
|
||||
For more, see [flake8-pie](https://pypi.org/project/flake8-pie/0.16.0/) on PyPI.
|
||||
For more, see [flake8-pie](https://pypi.org/project/flake8-pie/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PIE790 | no-unnecessary-pass | Unnecessary `pass` statement | 🛠 |
|
||||
| PIE794 | dupe-class-field-definitions | Class field `{name}` is defined multiple times | 🛠 |
|
||||
| PIE796 | prefer-unique-enums | Enum contains duplicate value: `{value}` | |
|
||||
| PIE807 | prefer-list-builtin | Prefer `list()` over useless lambda | 🛠 |
|
||||
| PIE800 | no-unnecessary-spread | Unnecessary spread `**` | |
|
||||
| PIE804 | no-unnecessary-dict-kwargs | Unnecessary `dict` kwargs | |
|
||||
| PIE807 | prefer-list-builtin | Prefer `list` over useless lambda | 🛠 |
|
||||
|
||||
### flake8-commas (COM)
|
||||
|
||||
For more, see [flake8-commas](https://pypi.org/project/flake8-commas/2.1.0/) on PyPI.
|
||||
For more, see [flake8-commas](https://pypi.org/project/flake8-commas/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -1160,12 +1178,76 @@ For more, see [flake8-commas](https://pypi.org/project/flake8-commas/2.1.0/) on
|
||||
|
||||
### flake8-no-pep420 (INP)
|
||||
|
||||
For more, see [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/2.3.0/) on PyPI.
|
||||
For more, see [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| INP001 | implicit-namespace-package | File `{filename}` is part of an implicit namespace package. Add an `__init__.py`. | |
|
||||
|
||||
### flake8-executable (EXE)
|
||||
|
||||
For more, see [flake8-executable](https://pypi.org/project/flake8-executable/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| EXE001 | shebang-not-executable | Shebang is present but file is not executable | |
|
||||
| EXE002 | shebang-missing-executable-file | The file is executable but no shebang is present | |
|
||||
| EXE003 | shebang-python | Shebang should contain "python" | |
|
||||
| EXE004 | shebang-whitespace | Avoid whitespace before shebang | 🛠 |
|
||||
| EXE005 | shebang-newline | Shebang should be at the beginning of the file | |
|
||||
|
||||
### flake8-type-checking (TYP)
|
||||
|
||||
For more, see [flake8-type-checking](https://pypi.org/project/flake8-type-checking/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| TYP005 | empty-type-checking-block | Found empty type-checking block | |
|
||||
|
||||
### tryceratops (TRY)
|
||||
|
||||
For more, see [tryceratops](https://pypi.org/project/tryceratops/1.1.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| TRY004 | prefer-type-error | Prefer `TypeError` exception for invalid type | 🛠 |
|
||||
| TRY200 | reraise-no-cause | Use `raise from` to specify exception cause | |
|
||||
| TRY201 | verbose-raise | Use `raise` without specifying exception name | |
|
||||
| TRY300 | try-consider-else | Consider `else` block | |
|
||||
| TRY301 | raise-within-try | Abstract `raise` to an inner function | |
|
||||
|
||||
### flake8-use-pathlib (PTH)
|
||||
|
||||
For more, see [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PTH100 | pathlib-abspath | `os.path.abspath` should be replaced by `.resolve()` | |
|
||||
| PTH101 | pathlib-chmod | `os.chmod` should be replaced by `.chmod()` | |
|
||||
| PTH102 | pathlib-mkdir | `os.mkdir` should be replaced by `.mkdir()` | |
|
||||
| PTH103 | pathlib-makedirs | `os.makedirs` should be replaced by `.mkdir(parents=True)` | |
|
||||
| PTH104 | pathlib-rename | `os.rename` should be replaced by `.rename()` | |
|
||||
| PTH105 | pathlib-replace | `os.replace`should be replaced by `.replace()` | |
|
||||
| PTH106 | pathlib-rmdir | `os.rmdir` should be replaced by `.rmdir()` | |
|
||||
| PTH107 | pathlib-remove | `os.remove` should be replaced by `.unlink()` | |
|
||||
| PTH108 | pathlib-unlink | `os.unlink` should be replaced by `.unlink()` | |
|
||||
| PTH109 | pathlib-getcwd | `os.getcwd()` should be replaced by `Path.cwd()` | |
|
||||
| PTH110 | pathlib-exists | `os.path.exists` should be replaced by `.exists()` | |
|
||||
| PTH111 | pathlib-expanduser | `os.path.expanduser` should be replaced by `.expanduser()` | |
|
||||
| PTH112 | pathlib-is-dir | `os.path.isdir` should be replaced by `.is_dir()` | |
|
||||
| PTH113 | pathlib-is-file | `os.path.isfile` should be replaced by `.is_file()` | |
|
||||
| PTH114 | pathlib-is-link | `os.path.islink` should be replaced by `.is_symlink()` | |
|
||||
| PTH115 | pathlib-readlink | `os.readlink(` should be replaced by `.readlink()` | |
|
||||
| PTH116 | pathlib-stat | `os.stat` should be replaced by `.stat()` or `.owner()` or `.group()` | |
|
||||
| PTH117 | pathlib-is-abs | `os.path.isabs` should be replaced by `.is_absolute()` | |
|
||||
| PTH118 | pathlib-join | `os.path.join` should be replaced by foo_path / "bar" | |
|
||||
| PTH119 | pathlib-basename | `os.path.basename` should be replaced by `.name` | |
|
||||
| PTH120 | pathlib-dirname | `os.path.dirname` should be replaced by `.parent` | |
|
||||
| PTH121 | pathlib-samefile | `os.path.samefile` should be replaced by `.samefile()` | |
|
||||
| PTH122 | pathlib-splitext | `os.path.splitext` should be replaced by `.suffix` | |
|
||||
| PTH123 | pathlib-open | `open("foo")` should be replaced by`Path("foo").open()` | |
|
||||
| PTH124 | pathlib-py-path | `py.path` is in maintenance mode, use `pathlib` instead | |
|
||||
|
||||
### Ruff-specific rules (RUF)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
@@ -1458,11 +1540,13 @@ natively, including:
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
|
||||
- [`flake8-executable`](https://pypi.org/project/flake8-executable/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-return`](https://pypi.org/project/flake8-return/)
|
||||
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
|
||||
@@ -1506,7 +1590,7 @@ Like Flake8, Pylint supports plugins (called "checkers"), while Ruff implements
|
||||
|
||||
Unlike Pylint, Ruff is capable of automatically fixing its own lint violations.
|
||||
|
||||
Pylint parity is being tracked in [#689](https://github.com/charliermarsh/ruff/issues/689).
|
||||
Pylint parity is being tracked in [#970](https://github.com/charliermarsh/ruff/issues/970).
|
||||
|
||||
### Which tools does Ruff replace?
|
||||
|
||||
@@ -1526,11 +1610,13 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-errmsg`](https://pypi.org/project/flake8-errmsg/)
|
||||
- [`flake8-executable`](https://pypi.org/project/flake8-executable/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-return`](https://pypi.org/project/flake8-return/)
|
||||
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
|
||||
@@ -1567,12 +1653,15 @@ project. See [#283](https://github.com/charliermarsh/ruff/issues/283) for more.
|
||||
### How does Ruff's import sorting compare to [`isort`](https://pypi.org/project/isort/)?
|
||||
|
||||
Ruff's import sorting is intended to be nearly equivalent to `isort` when used `profile = "black"`.
|
||||
(There are some minor differences in how Ruff and isort break ties between similar imports.)
|
||||
There are a few known, minor differences in how Ruff and isort break ties between similar imports,
|
||||
and in how Ruff and isort treat inline comments in some cases (see: [#1381](https://github.com/charliermarsh/ruff/issues/1381),
|
||||
[#2104](https://github.com/charliermarsh/ruff/issues/2104)).
|
||||
|
||||
Like `isort`, Ruff's import sorting is compatible with Black.
|
||||
|
||||
Ruff is less configurable than `isort`, but supports the `known-first-party`, `known-third-party`,
|
||||
`extra-standard-library`, and `src` settings, like so:
|
||||
Ruff does not yet support all of `isort`'s configuration options, though it does support many of
|
||||
them. You can find the supported settings in the [API reference](#isort). For example, you can set
|
||||
`known-first-party` like so:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
@@ -1639,24 +1728,10 @@ matter how they're provided, which avoids accidental incompatibilities and simpl
|
||||
|
||||
Run `ruff /path/to/code.py --show-settings` to view the resolved settings for a given file.
|
||||
|
||||
## Development
|
||||
## Contributing
|
||||
|
||||
Ruff is written in Rust (1.65.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:
|
||||
|
||||
```shell
|
||||
cargo run resources/test/fixtures
|
||||
```
|
||||
|
||||
For development, we use [nightly Rust](https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust):
|
||||
|
||||
```shell
|
||||
cargo +nightly fmt
|
||||
cargo +nightly clippy --fix --workspace --all-targets --all-features -- -W clippy::pedantic
|
||||
cargo +nightly test --all
|
||||
```
|
||||
Contributions are welcome and hugely appreciated. To get started, check out the
|
||||
[contributing guidelines](https://github.com/charliermarsh/ruff/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## Releases
|
||||
|
||||
@@ -1979,7 +2054,7 @@ recommended to only use `extend-ignore` when extending a
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<RuleCodePrefix>`
|
||||
**Type**: `Vec<RuleSelector>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2004,7 +2079,7 @@ recommended to only use `extend-select` when extending a
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<RuleCodePrefix>`
|
||||
**Type**: `Vec<RuleSelector>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2079,7 +2154,7 @@ A list of rule codes or prefixes to consider autofixable.
|
||||
|
||||
**Default value**: `["A", "ANN", "ARG", "B", "BLE", "C", "D", "E", "ERA", "F", "FBT", "I", "ICN", "N", "PGH", "PLC", "PLE", "PLR", "PLW", "Q", "RET", "RUF", "S", "T", "TID", "UP", "W", "YTT"]`
|
||||
|
||||
**Type**: `Vec<RuleCodePrefix>`
|
||||
**Type**: `Vec<RuleSelector>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2151,7 +2226,7 @@ specific prefixes.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<RuleCodePrefix>`
|
||||
**Type**: `Vec<RuleSelector>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2166,10 +2241,9 @@ ignore = ["F841"]
|
||||
#### [`ignore-init-module-imports`](#ignore-init-module-imports)
|
||||
|
||||
Avoid automatically removing unused imports in `__init__.py` files. Such
|
||||
imports will still be +flagged, but with a dedicated message
|
||||
suggesting that the import is either added to the module' +`__all__`
|
||||
symbol, or re-exported with a redundant alias (e.g., `import os as
|
||||
os`).
|
||||
imports will still be flagged, but with a dedicated message suggesting
|
||||
that the import is either added to the module's `__all__` symbol, or
|
||||
re-exported with a redundant alias (e.g., `import os as os`).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
@@ -2229,7 +2303,7 @@ exclude, when considering any matching files.
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
**Type**: `HashMap<String, Vec<RuleCodePrefix>>`
|
||||
**Type**: `HashMap<String, Vec<RuleSelector>>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2293,7 +2367,7 @@ specific prefixes.
|
||||
|
||||
**Default value**: `["E", "F"]`
|
||||
|
||||
**Type**: `Vec<RuleCodePrefix>`
|
||||
**Type**: `Vec<RuleSelector>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2437,7 +2511,7 @@ A list of rule codes or prefixes to consider non-autofix-able.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<RuleCodePrefix>`
|
||||
**Type**: `Vec<RuleSelector>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -2622,6 +2696,28 @@ max-string-length = 20
|
||||
|
||||
---
|
||||
|
||||
### `flake8-implicit-str-concat`
|
||||
|
||||
#### [`allow-multiline`](#allow-multiline)
|
||||
|
||||
Whether to allow implicit string concatenations for multiline strings.
|
||||
By default, implicit concatenations of multiline strings are
|
||||
allowed (but continuation lines, delimited with a backslash, are
|
||||
prohibited).
|
||||
|
||||
**Default value**: `true`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-implicit-str-concat]
|
||||
allow-multiline = false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `flake8-import-conventions`
|
||||
|
||||
#### [`aliases`](#aliases)
|
||||
@@ -3456,8 +3552,3 @@ keep-runtime-typing = true
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome and hugely appreciated. To get started, check out the
|
||||
[contributing guidelines](https://github.com/charliermarsh/ruff/blob/main/CONTRIBUTING.md).
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use ropey::Rope;
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let contents = fs::read_to_string(Path::new("resources/test/fixtures/D.py")).unwrap();
|
||||
c.bench_function("rope", |b| {
|
||||
b.iter(|| {
|
||||
let rope = Rope::from_str(black_box(&contents));
|
||||
rope.line_to_char(black_box(4));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
84
build.rs
84
build.rs
@@ -1,84 +0,0 @@
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn main() {
|
||||
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
generate_origin_name_and_url(&out_dir);
|
||||
}
|
||||
|
||||
const RULES_SUBMODULE_DOC_PREFIX: &str = "//! Rules from ";
|
||||
|
||||
/// The `src/rules/*/mod.rs` files are expected to have a first line such as the
|
||||
/// following:
|
||||
///
|
||||
/// //! Rules from [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/).
|
||||
///
|
||||
/// This function extracts the link label and url from these comments and
|
||||
/// generates the `name` and `url` functions for the `RuleOrigin` enum
|
||||
/// accordingly, so that they can be used by `ruff_dev::generate_rules_table`.
|
||||
fn generate_origin_name_and_url(out_dir: &Path) {
|
||||
println!("cargo:rerun-if-changed=src/rules/");
|
||||
|
||||
let mut name_match_arms: String = r#"RuleOrigin::Ruff => "Ruff-specific rules","#.into();
|
||||
let mut url_match_arms: String = r#"RuleOrigin::Ruff => None,"#.into();
|
||||
|
||||
for file in fs::read_dir("src/rules/")
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.filter(|f| f.file_type().unwrap().is_dir() && f.file_name() != "ruff")
|
||||
{
|
||||
let mod_rs_path = file.path().join("mod.rs");
|
||||
let mod_rs_path = mod_rs_path.to_str().unwrap();
|
||||
let first_line = BufReader::new(fs::File::open(mod_rs_path).unwrap())
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let Some(comment) = first_line.strip_prefix(RULES_SUBMODULE_DOC_PREFIX) else {
|
||||
panic!("expected first line in {mod_rs_path} to start with `{RULES_SUBMODULE_DOC_PREFIX}`")
|
||||
};
|
||||
let md_link = comment.trim_end_matches('.');
|
||||
|
||||
let (name, url) = md_link
|
||||
.strip_prefix('[')
|
||||
.unwrap()
|
||||
.strip_suffix(')')
|
||||
.unwrap()
|
||||
.split_once("](")
|
||||
.unwrap();
|
||||
|
||||
let dirname = file.file_name();
|
||||
let dirname = dirname.to_str().unwrap();
|
||||
|
||||
let variant_name = dirname
|
||||
.split('_')
|
||||
.map(|part| match part {
|
||||
"errmsg" => "ErrMsg".to_string(),
|
||||
"mccabe" => "McCabe".to_string(),
|
||||
"pep8" => "PEP8".to_string(),
|
||||
_ => format!("{}{}", part[..1].to_uppercase(), &part[1..]),
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
name_match_arms.push_str(&format!(r#"RuleOrigin::{variant_name} => "{name}","#));
|
||||
url_match_arms.push_str(&format!(r#"RuleOrigin::{variant_name} => Some("{url}"),"#));
|
||||
}
|
||||
|
||||
write!(
|
||||
BufWriter::new(fs::File::create(out_dir.join("origin.rs")).unwrap()),
|
||||
"
|
||||
impl RuleOrigin {{
|
||||
pub fn name(&self) -> &'static str {{
|
||||
match self {{ {name_match_arms} }}
|
||||
}}
|
||||
|
||||
pub fn url(&self) -> Option<&'static str> {{
|
||||
match self {{ {url_match_arms} }}
|
||||
}}
|
||||
}}
|
||||
"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -16,7 +16,7 @@ serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
toml_edit = { version = "0.17.1", features = ["easy"] }
|
||||
toml = { version = "0.6.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use std::path::PathBuf;
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use configparser::ini::Ini;
|
||||
use ruff::flake8_to_ruff;
|
||||
use ruff::flake8_to_ruff::{self, ExternalConfig};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
@@ -48,15 +48,19 @@ fn main() -> Result<()> {
|
||||
let config = ini.load(cli.file).map_err(|msg| anyhow::anyhow!(msg))?;
|
||||
|
||||
// Read the pyproject.toml file.
|
||||
let black = cli
|
||||
.pyproject
|
||||
.map(flake8_to_ruff::parse_black_options)
|
||||
.transpose()?
|
||||
.flatten();
|
||||
let pyproject = cli.pyproject.map(flake8_to_ruff::parse).transpose()?;
|
||||
let external_config = pyproject
|
||||
.as_ref()
|
||||
.and_then(|pyproject| pyproject.tool.as_ref())
|
||||
.map(|tool| ExternalConfig {
|
||||
black: tool.black.as_ref(),
|
||||
isort: tool.isort.as_ref(),
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
// Create Ruff's pyproject.toml section.
|
||||
let pyproject = flake8_to_ruff::convert(&config, black.as_ref(), cli.plugin)?;
|
||||
println!("{}", toml_edit::easy::to_string_pretty(&pyproject)?);
|
||||
let pyproject = flake8_to_ruff::convert(&config, &external_config, cli.plugin)?;
|
||||
println!("{}", toml::to_string_pretty(&pyproject)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
22
licenses/LICENSE_RoPP_flake8-use-pathlib
Normal file
22
licenses/LICENSE_RoPP_flake8-use-pathlib
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Rodolphe Pelloux-Prayer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
|
||||
8
resources/test/fixtures/flake8_bandit/S612.py
vendored
Normal file
8
resources/test/fixtures/flake8_bandit/S612.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import logging.config
|
||||
|
||||
t = logging.config.listen(9999)
|
||||
|
||||
def verify_func():
|
||||
pass
|
||||
|
||||
l = logging.config.listen(9999, verify=verify_func)
|
||||
@@ -1,5 +1,6 @@
|
||||
import some as sum
|
||||
from some import other as int
|
||||
from directory import new as dir
|
||||
|
||||
print = 1
|
||||
copyright: 'annotation' = 2
|
||||
@@ -7,6 +8,8 @@ copyright: 'annotation' = 2
|
||||
float = object = 4
|
||||
min, max = 5, 6
|
||||
|
||||
id = 4
|
||||
|
||||
def bytes():
|
||||
pass
|
||||
|
||||
|
||||
@@ -5,5 +5,7 @@ def func1(str, /, type, *complex, Exception, **getattr):
|
||||
async def func2(bytes):
|
||||
pass
|
||||
|
||||
async def func3(id, dir):
|
||||
pass
|
||||
|
||||
map([], lambda float: ...)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
class MyClass:
|
||||
ImportError = 4
|
||||
id = 5
|
||||
dir = "/"
|
||||
|
||||
def __init__(self):
|
||||
self.float = 5 # is fine
|
||||
self.id = 10
|
||||
self.dir = "."
|
||||
|
||||
def str(self):
|
||||
pass
|
||||
|
||||
@@ -383,7 +383,7 @@ s = (
|
||||
|
||||
s2 = (
|
||||
'this'
|
||||
'is a also a string'
|
||||
'is also a string'
|
||||
)
|
||||
|
||||
t = (
|
||||
|
||||
4
resources/test/fixtures/flake8_executable/EXE001_1.py
vendored
Normal file
4
resources/test/fixtures/flake8_executable/EXE001_1.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('I should be executable.')
|
||||
2
resources/test/fixtures/flake8_executable/EXE001_2.py
vendored
Normal file
2
resources/test/fixtures/flake8_executable/EXE001_2.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
if __name__ == '__main__':
|
||||
print('I should be executable.')
|
||||
4
resources/test/fixtures/flake8_executable/EXE001_3.py
vendored
Executable file
4
resources/test/fixtures/flake8_executable/EXE001_3.py
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('I should be executable.')
|
||||
2
resources/test/fixtures/flake8_executable/EXE002_1.py
vendored
Executable file
2
resources/test/fixtures/flake8_executable/EXE002_1.py
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
if __name__ == '__main__':
|
||||
print('I should be executable.')
|
||||
2
resources/test/fixtures/flake8_executable/EXE002_2.py
vendored
Normal file
2
resources/test/fixtures/flake8_executable/EXE002_2.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
if __name__ == '__main__':
|
||||
print('I should be executable.')
|
||||
4
resources/test/fixtures/flake8_executable/EXE002_3.py
vendored
Executable file
4
resources/test/fixtures/flake8_executable/EXE002_3.py
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('I should be executable.')
|
||||
2
resources/test/fixtures/flake8_executable/EXE003.py
vendored
Executable file
2
resources/test/fixtures/flake8_executable/EXE003.py
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/bash
|
||||
print("hello world")
|
||||
1
resources/test/fixtures/flake8_executable/EXE004_1.py
vendored
Executable file
1
resources/test/fixtures/flake8_executable/EXE004_1.py
vendored
Executable file
@@ -0,0 +1 @@
|
||||
#!/usr/bin/python
|
||||
2
resources/test/fixtures/flake8_executable/EXE004_2.py
vendored
Normal file
2
resources/test/fixtures/flake8_executable/EXE004_2.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
print(" #!/usr/bin/python")
|
||||
# shebang outside of comments should be ignored
|
||||
2
resources/test/fixtures/flake8_executable/EXE004_3.py
vendored
Executable file
2
resources/test/fixtures/flake8_executable/EXE004_3.py
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
|
||||
pass #!/usr/bin/env python
|
||||
3
resources/test/fixtures/flake8_executable/EXE005_1.py
vendored
Executable file
3
resources/test/fixtures/flake8_executable/EXE005_1.py
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
|
||||
# A python comment
|
||||
#!/usr/bin/python
|
||||
4
resources/test/fixtures/flake8_executable/EXE005_2.py
vendored
Executable file
4
resources/test/fixtures/flake8_executable/EXE005_2.py
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
print("code prior to shebang")
|
||||
|
||||
# A python comment
|
||||
#!/usr/bin/python
|
||||
6
resources/test/fixtures/flake8_executable/EXE005_3.py
vendored
Executable file
6
resources/test/fixtures/flake8_executable/EXE005_3.py
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
With a docstring
|
||||
print("commented out code")
|
||||
"""
|
||||
# A python comment
|
||||
#!/usr/bin/python
|
||||
21
resources/test/fixtures/flake8_import_conventions/from_imports.py
vendored
Normal file
21
resources/test/fixtures/flake8_import_conventions/from_imports.py
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Test absolute imports
|
||||
# Violation cases
|
||||
import xml.dom.minidom
|
||||
import xml.dom.minidom as wrong
|
||||
from xml.dom import minidom as wrong
|
||||
from xml.dom import minidom
|
||||
from xml.dom.minidom import parseString as wrong # Ensure ICN001 throws on function import.
|
||||
from xml.dom.minidom import parseString
|
||||
from xml.dom.minidom import parse, parseString
|
||||
from xml.dom.minidom import parse as ps, parseString as wrong
|
||||
|
||||
# No ICN001 violations
|
||||
import xml.dom.minidom as md
|
||||
from xml.dom import minidom as md
|
||||
from xml.dom.minidom import parseString as pstr
|
||||
from xml.dom.minidom import parse, parseString as pstr
|
||||
from xml.dom.minidom import parse as ps, parseString as pstr
|
||||
|
||||
|
||||
# Test relative imports
|
||||
from ..xml.dom import minidom as okay # Ensure config "xml.dom.minidom" doesn't catch relative imports
|
||||
0
resources/test/fixtures/flake8_no_pep420/test_pass_namespace_package/example.py
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass_namespace_package/example.py
vendored
Normal file
17
resources/test/fixtures/flake8_pie/PIE800.py
vendored
Normal file
17
resources/test/fixtures/flake8_pie/PIE800.py
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{"foo": 1, **{"bar": 1}} # PIE800
|
||||
|
||||
foo({**foo, **{"bar": True}}) # PIE800
|
||||
|
||||
{**foo, **{"bar": 10}} # PIE800
|
||||
|
||||
{**foo, **buzz, **{bar: 10}} # PIE800
|
||||
|
||||
{**foo, "bar": True } # OK
|
||||
|
||||
{"foo": 1, "buzz": {"bar": 1}} # OK
|
||||
|
||||
{**foo, "bar": True } # OK
|
||||
|
||||
Table.objects.filter(inst=inst, **{f"foo__{bar}__exists": True}) # OK
|
||||
|
||||
buzz = {**foo, "bar": { 1: 2 }} # OK
|
||||
19
resources/test/fixtures/flake8_pie/PIE804.py
vendored
Normal file
19
resources/test/fixtures/flake8_pie/PIE804.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
foo(**{"bar": True}) # PIE804
|
||||
|
||||
foo(**{"r2d2": True}) # PIE804
|
||||
|
||||
Foo.objects.create(**{"bar": True}) # PIE804
|
||||
|
||||
Foo.objects.create(**{"_id": some_id}) # PIE804
|
||||
|
||||
Foo.objects.create(**{**bar}) # PIE804
|
||||
|
||||
|
||||
foo(**{**data, "foo": "buzz"})
|
||||
foo(**buzz)
|
||||
foo(**{"bar-foo": True})
|
||||
foo(**{"bar foo": True})
|
||||
foo(**{"1foo": True})
|
||||
foo(**{buzz: True})
|
||||
foo(**{"": True})
|
||||
foo(**{f"buzz__{bar}": True})
|
||||
@@ -114,3 +114,12 @@ def bar3(x, y, z):
|
||||
else:
|
||||
return z
|
||||
return None
|
||||
|
||||
|
||||
def prompts(self, foo):
|
||||
if not foo:
|
||||
return []
|
||||
|
||||
for x in foo:
|
||||
yield x
|
||||
yield x + 1
|
||||
|
||||
@@ -3,6 +3,12 @@ if a:
|
||||
if b:
|
||||
c
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
if b:
|
||||
if c:
|
||||
d
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
pass
|
||||
@@ -86,3 +92,40 @@ if node.module:
|
||||
"multiprocessing."
|
||||
):
|
||||
print("Bad module!")
|
||||
|
||||
|
||||
# OK
|
||||
if a:
|
||||
if b:
|
||||
print("foo")
|
||||
else:
|
||||
print("bar")
|
||||
|
||||
# OK
|
||||
if a:
|
||||
if b:
|
||||
if c:
|
||||
print("foo")
|
||||
else:
|
||||
print("bar")
|
||||
else:
|
||||
print("bar")
|
||||
|
||||
# OK
|
||||
if a:
|
||||
# SIM 102
|
||||
if b:
|
||||
if c:
|
||||
print("foo")
|
||||
else:
|
||||
print("bar")
|
||||
|
||||
|
||||
# OK
|
||||
if a:
|
||||
if b:
|
||||
if c:
|
||||
print("foo")
|
||||
print("baz")
|
||||
else:
|
||||
print("bar")
|
||||
|
||||
@@ -42,3 +42,11 @@ def f():
|
||||
return "foo"
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103 (but not fixable)
|
||||
if a:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
25
resources/test/fixtures/flake8_type_checking/TYP005.py
vendored
Normal file
25
resources/test/fixtures/flake8_type_checking/TYP005.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
|
||||
|
||||
def example():
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
return
|
||||
|
||||
|
||||
class Test:
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
x = 2
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
if 2:
|
||||
pass
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
x: List
|
||||
31
resources/test/fixtures/flake8_use_pathlib/full_name.py
vendored
Normal file
31
resources/test/fixtures/flake8_use_pathlib/full_name.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import os.path
|
||||
|
||||
p = "/foo"
|
||||
|
||||
a = os.path.abspath(p)
|
||||
aa = os.chmod(p)
|
||||
aaa = os.mkdir(p)
|
||||
os.makedirs(p)
|
||||
os.rename(p)
|
||||
os.replace(p)
|
||||
os.rmdir(p)
|
||||
os.remove(p)
|
||||
os.unlink(p)
|
||||
os.getcwd(p)
|
||||
b = os.path.exists(p)
|
||||
bb = os.path.expanduser(p)
|
||||
bbb = os.path.isdir(p)
|
||||
bbbb = os.path.isfile(p)
|
||||
bbbbb = os.path.islink(p)
|
||||
os.readlink(p)
|
||||
os.stat(p)
|
||||
os.path.isabs(p)
|
||||
os.path.join(p)
|
||||
os.path.basename(p)
|
||||
os.path.dirname(p)
|
||||
os.path.samefile(p)
|
||||
os.path.splitext(p)
|
||||
with open(p) as fp:
|
||||
fp.read()
|
||||
open(p).close()
|
||||
28
resources/test/fixtures/flake8_use_pathlib/import_as.py
vendored
Normal file
28
resources/test/fixtures/flake8_use_pathlib/import_as.py
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import os as foo
|
||||
import os.path as foo_p
|
||||
|
||||
p = "/foo"
|
||||
|
||||
a = foo_p.abspath(p)
|
||||
aa = foo.chmod(p)
|
||||
aaa = foo.mkdir(p)
|
||||
foo.makedirs(p)
|
||||
foo.rename(p)
|
||||
foo.replace(p)
|
||||
foo.rmdir(p)
|
||||
foo.remove(p)
|
||||
foo.unlink(p)
|
||||
foo.getcwd(p)
|
||||
b = foo_p.exists(p)
|
||||
bb = foo_p.expanduser(p)
|
||||
bbb = foo_p.isdir(p)
|
||||
bbbb = foo_p.isfile(p)
|
||||
bbbbb = foo_p.islink(p)
|
||||
foo.readlink(p)
|
||||
foo.stat(p)
|
||||
foo_p.isabs(p)
|
||||
foo_p.join(p)
|
||||
foo_p.basename(p)
|
||||
foo_p.dirname(p)
|
||||
foo_p.samefile(p)
|
||||
foo_p.splitext(p)
|
||||
33
resources/test/fixtures/flake8_use_pathlib/import_from.py
vendored
Normal file
33
resources/test/fixtures/flake8_use_pathlib/import_from.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
from os import chmod, mkdir, makedirs, rename, replace, rmdir
|
||||
from os import remove, unlink, getcwd, readlink, stat
|
||||
from os.path import abspath, exists, expanduser, isdir, isfile, islink
|
||||
from os.path import isabs, join, basename, dirname, samefile, splitext
|
||||
|
||||
p = "/foo"
|
||||
|
||||
a = abspath(p)
|
||||
aa = chmod(p)
|
||||
aaa = mkdir(p)
|
||||
makedirs(p)
|
||||
rename(p)
|
||||
replace(p)
|
||||
rmdir(p)
|
||||
remove(p)
|
||||
unlink(p)
|
||||
getcwd(p)
|
||||
b = exists(p)
|
||||
bb = expanduser(p)
|
||||
bbb = isdir(p)
|
||||
bbbb = isfile(p)
|
||||
bbbbb = islink(p)
|
||||
readlink(p)
|
||||
stat(p)
|
||||
isabs(p)
|
||||
join(p)
|
||||
basename(p)
|
||||
dirname(p)
|
||||
samefile(p)
|
||||
splitext(p)
|
||||
with open(p) as fp:
|
||||
fp.read()
|
||||
open(p).close()
|
||||
35
resources/test/fixtures/flake8_use_pathlib/import_from_as.py
vendored
Normal file
35
resources/test/fixtures/flake8_use_pathlib/import_from_as.py
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
from os import chmod as xchmod, mkdir as xmkdir
|
||||
from os import makedirs as xmakedirs, rename as xrename, replace as xreplace
|
||||
from os import rmdir as xrmdir, remove as xremove, unlink as xunlink
|
||||
from os import getcwd as xgetcwd, readlink as xreadlink, stat as xstat
|
||||
from os.path import abspath as xabspath, exists as xexists
|
||||
from os.path import expanduser as xexpanduser, isdir as xisdir
|
||||
from os.path import isfile as xisfile, islink as xislink, isabs as xisabs
|
||||
from os.path import join as xjoin, basename as xbasename, dirname as xdirname
|
||||
from os.path import samefile as xsamefile, splitext as xsplitext
|
||||
|
||||
p = "/foo"
|
||||
|
||||
a = xabspath(p)
|
||||
aa = xchmod(p)
|
||||
aaa = xmkdir(p)
|
||||
xmakedirs(p)
|
||||
xrename(p)
|
||||
xreplace(p)
|
||||
xrmdir(p)
|
||||
xremove(p)
|
||||
xunlink(p)
|
||||
xgetcwd(p)
|
||||
b = xexists(p)
|
||||
bb = xexpanduser(p)
|
||||
bbb = xisdir(p)
|
||||
bbbb = xisfile(p)
|
||||
bbbbb = xislink(p)
|
||||
xreadlink(p)
|
||||
xstat(p)
|
||||
xisabs(p)
|
||||
xjoin(p)
|
||||
xbasename(p)
|
||||
xdirname(p)
|
||||
xsamefile(p)
|
||||
xsplitext(p)
|
||||
3
resources/test/fixtures/flake8_use_pathlib/py_path_1.py
vendored
Normal file
3
resources/test/fixtures/flake8_use_pathlib/py_path_1.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import py
|
||||
|
||||
p = py.path.local("../foo")
|
||||
3
resources/test/fixtures/flake8_use_pathlib/py_path_2.py
vendored
Normal file
3
resources/test/fixtures/flake8_use_pathlib/py_path_2.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
from py.path import local as path
|
||||
|
||||
p = path("/foo")
|
||||
@@ -1 +0,0 @@
|
||||
from long_module_name import member_one, member_two, member_three, member_four, member_five
|
||||
6
resources/test/fixtures/pep8_naming/N806.py
vendored
6
resources/test/fixtures/pep8_naming/N806.py
vendored
@@ -1,5 +1,7 @@
|
||||
import collections
|
||||
from collections import namedtuple
|
||||
from typing import TypeVar
|
||||
from typing import NewType
|
||||
|
||||
GLOBAL: str = "foo"
|
||||
|
||||
@@ -11,5 +13,9 @@ def f():
|
||||
Camel = 0
|
||||
CONSTANT = 0
|
||||
_ = 0
|
||||
|
||||
MyObj1 = collections.namedtuple("MyObj1", ["a", "b"])
|
||||
MyObj2 = namedtuple("MyObj12", ["a", "b"])
|
||||
|
||||
T = TypeVar("T")
|
||||
UserId = NewType('UserId', int)
|
||||
|
||||
19
resources/test/fixtures/pycodestyle/E101.py
vendored
Normal file
19
resources/test/fixtures/pycodestyle/E101.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
def func_all_spaces():
|
||||
# No error
|
||||
print("spaces")
|
||||
|
||||
def func_tabs():
|
||||
# No error
|
||||
print("tabs")
|
||||
|
||||
def func_mixed_start_with_tab():
|
||||
# E101
|
||||
print("mixed starts with tab")
|
||||
|
||||
def func_mixed_start_with_space():
|
||||
# E101
|
||||
print("mixed starts with space")
|
||||
|
||||
def xyz():
|
||||
# E101
|
||||
print("xyz");
|
||||
28
resources/test/fixtures/pydocstyle/D.py
vendored
28
resources/test/fixtures/pydocstyle/D.py
vendored
@@ -606,3 +606,31 @@ def one_liner():
|
||||
r"""Wrong.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
"""Wrong."
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
"""
|
||||
|
||||
"Wrong."""
|
||||
|
||||
|
||||
@expect('D404: First word of the docstring should not be "This"')
|
||||
def starts_with_this():
|
||||
"""This is a docstring."""
|
||||
|
||||
|
||||
@expect('D404: First word of the docstring should not be "This"')
|
||||
def starts_with_space_then_this():
|
||||
""" This is a docstring that starts with a space.""" # noqa: D210
|
||||
|
||||
101
resources/test/fixtures/pydocstyle/D401.py
vendored
Normal file
101
resources/test/fixtures/pydocstyle/D401.py
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
"""This module docstring does not need to be written in imperative mood."""
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
# Bad examples
|
||||
|
||||
def bad_liouiwnlkjl():
|
||||
"""Returns foo."""
|
||||
|
||||
|
||||
def bad_sdgfsdg23245():
|
||||
"""Constructor for a foo."""
|
||||
|
||||
|
||||
def bad_sdgfsdg23245777():
|
||||
"""
|
||||
|
||||
Constructor for a boa.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def bad_run_something():
|
||||
"""Runs something"""
|
||||
|
||||
def bad_nested():
|
||||
"""Runs other things, nested"""
|
||||
|
||||
bad_nested()
|
||||
|
||||
|
||||
def multi_line():
|
||||
"""Writes a logical line that
|
||||
extends to two physical lines.
|
||||
"""
|
||||
|
||||
|
||||
# Good examples
|
||||
|
||||
def good_run_something():
|
||||
"""Run away."""
|
||||
|
||||
def good_nested():
|
||||
"""Run to the hills."""
|
||||
|
||||
good_nested()
|
||||
|
||||
|
||||
def good_construct():
|
||||
"""Construct a beautiful house."""
|
||||
|
||||
|
||||
def good_multi_line():
|
||||
"""Write a logical line that
|
||||
extends to two physical lines.
|
||||
"""
|
||||
|
||||
|
||||
good_top_level_var = False
|
||||
"""This top level assignment attribute docstring does not need to be written in imperative mood."""
|
||||
|
||||
|
||||
# Classes and their members
|
||||
|
||||
class Thingy:
|
||||
"""This class docstring does not need to be written in imperative mood."""
|
||||
|
||||
_beep = "boop"
|
||||
"""This class attribute docstring does not need to be written in imperative mood."""
|
||||
|
||||
def bad_method(self):
|
||||
"""This method docstring should be written in imperative mood."""
|
||||
|
||||
@property
|
||||
def good_property(self):
|
||||
"""This property method docstring does not need to be written in imperative mood."""
|
||||
return self._beep
|
||||
|
||||
@cached_property
|
||||
def good_cached_property(self):
|
||||
"""This property method docstring does not need to be written in imperative mood."""
|
||||
return 42 * 42
|
||||
|
||||
class NestedThingy:
|
||||
"""This nested class docstring does not need to be written in imperative mood."""
|
||||
|
||||
|
||||
# Test functions
|
||||
|
||||
def test_something():
|
||||
"""This test function does not need to be written in imperative mood.
|
||||
|
||||
pydocstyle's rationale:
|
||||
We exclude tests from the imperative mood check, because to phrase
|
||||
their docstring in the imperative mood, they would have to start with
|
||||
a highly redundant "Test that ..."
|
||||
"""
|
||||
|
||||
|
||||
def runTest():
|
||||
"""This test function does not need to be written in imperative mood, either."""
|
||||
@@ -1,51 +1,51 @@
|
||||
|
||||
if True:
|
||||
import foo; x = 1
|
||||
import foo; x = 1
|
||||
import foo1; x = 1
|
||||
import foo2; x = 1
|
||||
|
||||
if True:
|
||||
import foo; \
|
||||
import foo3; \
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
import foo \
|
||||
import foo4 \
|
||||
; x = 1
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; import foo
|
||||
x = 1; import foo5
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
import foo
|
||||
import foo6
|
||||
|
||||
|
||||
if True:
|
||||
x = 1 \
|
||||
; import foo
|
||||
; import foo7
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; import foo; x = 1
|
||||
x = 1; import foo; x = 1
|
||||
x = 1; import foo8; x = 1
|
||||
x = 1; import foo9; x = 1
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
import foo; \
|
||||
import foo10; \
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
x = 1 \
|
||||
;import foo \
|
||||
;import foo11 \
|
||||
;x = 1
|
||||
|
||||
|
||||
# Continuation, but not as the last content in the file.
|
||||
x = 1; \
|
||||
import foo
|
||||
import foo12
|
||||
|
||||
# Continuation, followed by end-of-file. (Removing `import foo` would cause a syntax
|
||||
# error.)
|
||||
x = 1; \
|
||||
import foo
|
||||
import foo13
|
||||
|
||||
3
resources/test/fixtures/pyproject.toml
vendored
3
resources/test/fixtures/pyproject.toml
vendored
@@ -12,6 +12,9 @@ per-file-ignores = { "__init__.py" = ["F401"] }
|
||||
[tool.ruff.flake8-bugbear]
|
||||
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
|
||||
[tool.ruff.flake8-builtins]
|
||||
builtins-ignorelist = ["id", "dir"]
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
multiline-quotes = "double"
|
||||
|
||||
4
resources/test/fixtures/pyupgrade/UP013.py
vendored
4
resources/test/fixtures/pyupgrade/UP013.py
vendored
@@ -31,3 +31,7 @@ MyType10 = TypedDict("MyType10", {"key": Literal["value"]})
|
||||
|
||||
# using namespace TypedDict
|
||||
MyType11 = typing.TypedDict("MyType11", {"key": int})
|
||||
|
||||
# unpacking
|
||||
c = {"c": float}
|
||||
MyType12 = TypedDict("MyType1", {"a": int, "b": str, **c})
|
||||
|
||||
75
resources/test/fixtures/pyupgrade/UP031_0.py
vendored
Normal file
75
resources/test/fixtures/pyupgrade/UP031_0.py
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
a, b, x, y = 1, 2, 3, 4
|
||||
|
||||
# UP031
|
||||
print('%s %s' % (a, b))
|
||||
|
||||
print('%s%s' % (a, b))
|
||||
|
||||
print("trivial" % ())
|
||||
|
||||
print("%s" % ("simple",))
|
||||
|
||||
print("%s" % ("%s" % ("nested",),))
|
||||
|
||||
print("%s%% percent" % (15,))
|
||||
|
||||
print("%f" % (15,))
|
||||
|
||||
print("%.f" % (15,))
|
||||
|
||||
print("%.3f" % (15,))
|
||||
|
||||
print("%3f" % (15,))
|
||||
|
||||
print("%-5f" % (5,))
|
||||
|
||||
print("%9f" % (5,))
|
||||
|
||||
print("%#o" % (123,))
|
||||
|
||||
print("brace {} %s" % (1,))
|
||||
|
||||
print(
|
||||
"%s" % (
|
||||
"trailing comma",
|
||||
)
|
||||
)
|
||||
|
||||
print("foo %s " % (x,))
|
||||
|
||||
print("%(k)s" % {"k": "v"})
|
||||
|
||||
print("%(k)s" % {
|
||||
"k": "v",
|
||||
"i": "j"
|
||||
})
|
||||
|
||||
print("%(to_list)s" % {"to_list": []})
|
||||
|
||||
print("%(k)s" % {"k": "v", "i": 1, "j": []})
|
||||
|
||||
print("%(ab)s" % {"a" "b": 1})
|
||||
|
||||
print("%(a)s" % {"a" : 1})
|
||||
|
||||
print((
|
||||
"foo %s "
|
||||
"bar %s" % (x, y)
|
||||
))
|
||||
|
||||
print(
|
||||
"foo %(foo)s "
|
||||
"bar %(bar)s" % {"foo": x, "bar": y}
|
||||
)
|
||||
|
||||
bar = {"bar": y}
|
||||
print(
|
||||
"foo %(foo)s "
|
||||
"bar %(bar)s" % {"foo": x, **bar}
|
||||
)
|
||||
|
||||
print("%s \N{snowman}" % (a,))
|
||||
|
||||
print("%(foo)s \N{snowman}" % {"foo": 1})
|
||||
|
||||
print(("foo %s " "bar %s") % (x, y))
|
||||
59
resources/test/fixtures/pyupgrade/UP031_1.py
vendored
Normal file
59
resources/test/fixtures/pyupgrade/UP031_1.py
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
# OK
|
||||
"%s" % unknown_type
|
||||
|
||||
b"%s" % (b"bytestring",)
|
||||
|
||||
"%*s" % (5, "hi")
|
||||
|
||||
"%d" % (flt,)
|
||||
|
||||
"%c" % (some_string,)
|
||||
|
||||
"%4%" % ()
|
||||
|
||||
"%.2r" % (1.25)
|
||||
|
||||
i % 3
|
||||
|
||||
"%.*s" % (5, "hi")
|
||||
|
||||
"%i" % (flt,)
|
||||
|
||||
"%()s" % {"": "empty"}
|
||||
|
||||
"%s" % {"k": "v"}
|
||||
|
||||
"%(1)s" % {"1": "bar"}
|
||||
|
||||
"%(a)s" % {"a": 1, "a": 2}
|
||||
|
||||
pytest.param('"%8s" % (None,)', id="unsafe width-string conversion"),
|
||||
|
||||
"%()s" % {"": "bar"}
|
||||
|
||||
"%(1)s" % {1: 2, "1": 2}
|
||||
|
||||
"%(and)s" % {"and": 2}
|
||||
|
||||
# OK (arguably false negatives)
|
||||
(
|
||||
"foo %s "
|
||||
"bar %s"
|
||||
) % (x, y)
|
||||
|
||||
(
|
||||
"foo %(foo)s "
|
||||
"bar %(bar)s"
|
||||
) % {"foo": x, "bar": y}
|
||||
|
||||
(
|
||||
"""foo %s"""
|
||||
% (x,)
|
||||
)
|
||||
|
||||
(
|
||||
"""
|
||||
foo %s
|
||||
"""
|
||||
% (x,)
|
||||
)
|
||||
2
resources/test/fixtures/pyupgrade/UP032.py
vendored
2
resources/test/fixtures/pyupgrade/UP032.py
vendored
@@ -44,6 +44,8 @@ print("foo {} ".format(x))
|
||||
|
||||
"{}".format(a)
|
||||
|
||||
'({}={{0!e}})'.format(a)
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
|
||||
61
resources/test/fixtures/pyupgrade/UP034.py
vendored
Normal file
61
resources/test/fixtures/pyupgrade/UP034.py
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# UP034
|
||||
print(("foo"))
|
||||
|
||||
# UP034
|
||||
print(("hell((goodybe))o"))
|
||||
|
||||
# UP034
|
||||
print((("foo")))
|
||||
|
||||
# UP034
|
||||
print((((1))))
|
||||
|
||||
# UP034
|
||||
print(("foo{}".format(1)))
|
||||
|
||||
# UP034
|
||||
print(
|
||||
("foo{}".format(1))
|
||||
)
|
||||
|
||||
# UP034
|
||||
print(
|
||||
(
|
||||
"foo"
|
||||
)
|
||||
)
|
||||
|
||||
# UP034
|
||||
def f():
|
||||
x = int(((yield 1)))
|
||||
|
||||
# UP034
|
||||
if True:
|
||||
print(
|
||||
("foo{}".format(1))
|
||||
)
|
||||
|
||||
# UP034
|
||||
print((x for x in range(3)))
|
||||
|
||||
# OK
|
||||
print("foo")
|
||||
|
||||
# OK
|
||||
print((1, 2, 3))
|
||||
|
||||
# OK
|
||||
print(())
|
||||
|
||||
# OK
|
||||
print((1,))
|
||||
|
||||
# OK
|
||||
sum((block.code for block in blocks), [])
|
||||
|
||||
# OK
|
||||
def f():
|
||||
x = int((yield 1))
|
||||
|
||||
# OK
|
||||
sum((i for i in range(3)), [])
|
||||
17
resources/test/fixtures/ruff/RUF005.py
vendored
17
resources/test/fixtures/ruff/RUF005.py
vendored
@@ -20,3 +20,20 @@ astonishment = ("we all feel", ) + Fun.words
|
||||
chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob)
|
||||
|
||||
baz = () + zoob
|
||||
|
||||
first = [
|
||||
# The order
|
||||
1, # here
|
||||
2, # is
|
||||
# extremely
|
||||
3, # critical
|
||||
# to preserve
|
||||
]
|
||||
second = first + [
|
||||
# please
|
||||
4,
|
||||
# don't
|
||||
5,
|
||||
# touch
|
||||
6,
|
||||
]
|
||||
|
||||
129
resources/test/fixtures/ruff/RUF100_1.py
vendored
129
resources/test/fixtures/ruff/RUF100_1.py
vendored
@@ -1,64 +1,89 @@
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa: F401
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa: F401
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa: F401
|
||||
Sequence, # noqa: F401
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa
|
||||
Sequence, # noqa
|
||||
)
|
||||
|
||||
# This should ignore the first error.
|
||||
from typing import (
|
||||
Mapping, # noqa: F401
|
||||
Union,
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa: F401
|
||||
Sequence, # noqa: F401
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa
|
||||
Sequence, # noqa
|
||||
)
|
||||
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Dict, # noqa: F501
|
||||
)
|
||||
def f():
|
||||
# This should ignore the first error.
|
||||
from typing import (
|
||||
Mapping, # noqa: F401
|
||||
Union,
|
||||
)
|
||||
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F501
|
||||
Tuple, # noqa: F401
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import Any, AnyStr # noqa: F401
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import AsyncIterable, AsyncGenerator # noqa
|
||||
|
||||
# This should mark F501 as unused.
|
||||
from typing import Awaitable, AwaitableGenerator # noqa: F501
|
||||
def f():
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Dict, # noqa: F501
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F501
|
||||
Tuple, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import Any, AnyStr # noqa: F401
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import AsyncIterable, AsyncGenerator # noqa
|
||||
|
||||
|
||||
def f():
|
||||
# This should mark F501 as unused.
|
||||
from typing import Awaitable, AwaitableGenerator # noqa: F501
|
||||
|
||||
296
resources/test/fixtures/tryceratops/TRY004.py
vendored
Normal file
296
resources/test/fixtures/tryceratops/TRY004.py
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Prefer TypeError when relevant.
|
||||
"""
|
||||
|
||||
|
||||
def incorrect_basic(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_multiple_type_check(some_arg):
|
||||
if isinstance(some_arg, (int, str)):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
class MyClass:
|
||||
pass
|
||||
|
||||
|
||||
def incorrect_with_issubclass(some_arg):
|
||||
if issubclass(some_arg, MyClass):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_with_callable(some_arg):
|
||||
if callable(some_arg):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_ArithmeticError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise ArithmeticError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_AssertionError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_AttributeError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise AttributeError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_BufferError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise BufferError # should be typeerror
|
||||
|
||||
|
||||
def incorrect_EOFError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise EOFError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_ImportError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise ImportError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_LookupError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise LookupError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_MemoryError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
# should be typeerror
|
||||
# not multiline is on purpose for fix
|
||||
raise MemoryError(
|
||||
"..."
|
||||
)
|
||||
|
||||
|
||||
def incorrect_NameError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise NameError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_ReferenceError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise ReferenceError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_RuntimeError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_SyntaxError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise SyntaxError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_SystemError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise SystemError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_ValueError(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise ValueError("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_not_operator_isinstance(some_arg):
|
||||
if not isinstance(some_arg):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_and_operator_isinstance(arg1, arg2):
|
||||
if isinstance(some_arg) and isinstance(arg2):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_or_operator_isinstance(arg1, arg2):
|
||||
if isinstance(some_arg) or isinstance(arg2):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_multiple_operators_isinstance(arg1, arg2, arg3):
|
||||
if not isinstance(arg1) and isinstance(arg2) or isinstance(arg3):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_not_operator_callable(some_arg):
|
||||
if not callable(some_arg):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_and_operator_callable(arg1, arg2):
|
||||
if callable(some_arg) and callable(arg2):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_or_operator_callable(arg1, arg2):
|
||||
if callable(some_arg) or callable(arg2):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_multiple_operators_callable(arg1, arg2, arg3):
|
||||
if not callable(arg1) and callable(arg2) or callable(arg3):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_not_operator_issubclass(some_arg):
|
||||
if not issubclass(some_arg):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_and_operator_issubclass(arg1, arg2):
|
||||
if issubclass(some_arg) and issubclass(arg2):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_or_operator_issubclass(arg1, arg2):
|
||||
if issubclass(some_arg) or issubclass(arg2):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_multiple_operators_issubclass(arg1, arg2, arg3):
|
||||
if not issubclass(arg1) and issubclass(arg2) or issubclass(arg3):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
def incorrect_multi_conditional(arg1, arg2):
|
||||
if isinstance(arg1, int):
|
||||
pass
|
||||
elif isinstance(arg2, int):
|
||||
raise Exception("...") # should be typeerror
|
||||
|
||||
|
||||
class MyCustomTypeValidation(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def correct_custom_exception(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise MyCustomTypeValidation("...") # that's correct, because it's not vanilla
|
||||
|
||||
|
||||
def correct_complex_conditional(val):
|
||||
if val is not None and (not isinstance(val, int) or val < 0):
|
||||
raise ValueError(...) # fine if this is not a TypeError
|
||||
|
||||
|
||||
def correct_multi_conditional(some_arg):
|
||||
if some_arg == 3:
|
||||
pass
|
||||
elif isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise Exception("...") # fine if this is not a TypeError
|
||||
|
||||
|
||||
def correct_should_ignore(some_arg):
|
||||
if isinstance(some_arg, int):
|
||||
pass
|
||||
else:
|
||||
raise TypeError("...")
|
||||
|
||||
|
||||
def check_body(some_args):
|
||||
if isinstance(some_args, int):
|
||||
raise ValueError("...") # should be typeerror
|
||||
|
||||
|
||||
def check_body_correct(some_args):
|
||||
if isinstance(some_args, int):
|
||||
raise TypeError("...") # correct
|
||||
|
||||
|
||||
def multiple_elifs(some_args):
|
||||
if not isinstance(some_args, int):
|
||||
raise ValueError("...") # should be typerror
|
||||
elif some_args < 3:
|
||||
raise ValueError("...") # this is ok
|
||||
elif some_args > 10:
|
||||
raise ValueError("...") # this is ok if we don't simplify
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def multiple_ifs(some_args):
|
||||
if not isinstance(some_args, int):
|
||||
raise ValueError("...") # should be typerror
|
||||
else:
|
||||
if some_args < 3:
|
||||
raise ValueError("...") # this is ok
|
||||
else:
|
||||
if some_args > 10:
|
||||
raise ValueError("...") # this is ok if we don't simplify
|
||||
else:
|
||||
pass
|
||||
27
resources/test/fixtures/tryceratops/TRY200.py
vendored
Normal file
27
resources/test/fixtures/tryceratops/TRY200.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MainFunctionFailed(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def process():
|
||||
raise MyException
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except MyException:
|
||||
raise MainFunctionFailed()
|
||||
|
||||
if True:
|
||||
raise MainFunctionFailed()
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
process()
|
||||
except MyException as ex:
|
||||
raise MainFunctionFailed() from ex
|
||||
54
resources/test/fixtures/tryceratops/TRY201.py
vendored
Normal file
54
resources/test/fixtures/tryceratops/TRY201.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Raising an exception using its assigned name is verbose and unrequired
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
logger.exception("process failed")
|
||||
raise e
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
raise
|
||||
|
||||
def still_good():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
print(e)
|
||||
raise
|
||||
|
||||
|
||||
def bad_that_needs_recursion():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
logger.exception("process failed")
|
||||
if True:
|
||||
raise e
|
||||
|
||||
|
||||
def bad_that_needs_recursion_2():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
logger.exception("process failed")
|
||||
if True:
|
||||
def foo():
|
||||
raise e
|
||||
47
resources/test/fixtures/tryceratops/TRY300.py
vendored
Normal file
47
resources/test/fixtures/tryceratops/TRY300.py
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Returning a final value inside a try block may indicate you could use an else block
|
||||
instead to outline the success scenario
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
b = process()
|
||||
return b
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
a = 1
|
||||
b = process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
else:
|
||||
return b
|
||||
|
||||
|
||||
def noreturn():
|
||||
try:
|
||||
a = 1
|
||||
b = process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
|
||||
|
||||
def still_good():
|
||||
try:
|
||||
return process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
29
resources/test/fixtures/tryceratops/TRY301.py
vendored
Normal file
29
resources/test/fixtures/tryceratops/TRY301.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = process()
|
||||
if not a:
|
||||
raise MyException(a)
|
||||
|
||||
raise MyException(a)
|
||||
|
||||
try:
|
||||
b = process()
|
||||
if not b:
|
||||
raise MyException(b)
|
||||
except Exception:
|
||||
logger.exception("something failed")
|
||||
except Exception:
|
||||
logger.exception("something failed")
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
a = process() # This throws the exception now
|
||||
except MyException:
|
||||
logger.exception("a failed")
|
||||
except Exception:
|
||||
logger.exception("something failed")
|
||||
198
ruff.schema.json
198
ruff.schema.json
@@ -73,7 +73,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"extend-select": {
|
||||
@@ -83,7 +83,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
@@ -117,7 +117,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"flake8-annotations": {
|
||||
@@ -153,6 +153,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-builtins": {
|
||||
"description": "Options for the `flake8-builtins` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Flake8BuiltinsOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-errmsg": {
|
||||
"description": "Options for the `flake8-errmsg` plugin.",
|
||||
"anyOf": [
|
||||
@@ -164,6 +175,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-implicit-str-concat": {
|
||||
"description": "Options for the `flake8-implicit-str-concat` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Flake8ImplicitStrConcatOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-import-conventions": {
|
||||
"description": "Options for the `flake8-import-conventions` plugin.",
|
||||
"anyOf": [
|
||||
@@ -244,11 +266,11 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"ignore-init-module-imports": {
|
||||
"description": "Avoid automatically removing unused imports in `__init__.py` files. Such imports will still be +flagged, but with a dedicated message suggesting that the import is either added to the module' +`__all__` symbol, or re-exported with a redundant alias (e.g., `import os as os`).",
|
||||
"description": "Avoid automatically removing unused imports in `__init__.py` files. Such imports will still be flagged, but with a dedicated message suggesting that the import is either added to the module's `__all__` symbol, or re-exported with a redundant alias (e.g., `import os as os`).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -315,7 +337,7 @@
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -388,7 +410,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"show-source": {
|
||||
@@ -446,7 +468,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"update-check": {
|
||||
@@ -584,6 +606,22 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8BuiltinsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"builtins-ignorelist": {
|
||||
"description": "Ignore list of builtins.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8ErrMsgOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -599,6 +637,19 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8ImplicitStrConcatOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow-multiline": {
|
||||
"description": "Whether to allow implicit string concatenations for multiline strings. By default, implicit concatenations of multiline strings are allowed (but continuation lines, delimited with a backslash, are prohibited).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8ImportConventionsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1134,16 +1185,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RuleCodePrefix": {
|
||||
"RuleSelector": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ALL",
|
||||
"A",
|
||||
"A0",
|
||||
"A00",
|
||||
"A001",
|
||||
"A002",
|
||||
"A003",
|
||||
"ALL",
|
||||
"ANN",
|
||||
"ANN0",
|
||||
"ANN00",
|
||||
@@ -1277,6 +1328,7 @@
|
||||
"D4",
|
||||
"D40",
|
||||
"D400",
|
||||
"D401",
|
||||
"D402",
|
||||
"D403",
|
||||
"D404",
|
||||
@@ -1310,6 +1362,9 @@
|
||||
"DTZ011",
|
||||
"DTZ012",
|
||||
"E",
|
||||
"E1",
|
||||
"E10",
|
||||
"E101",
|
||||
"E4",
|
||||
"E40",
|
||||
"E401",
|
||||
@@ -1347,6 +1402,14 @@
|
||||
"ERA0",
|
||||
"ERA00",
|
||||
"ERA001",
|
||||
"EXE",
|
||||
"EXE0",
|
||||
"EXE00",
|
||||
"EXE001",
|
||||
"EXE002",
|
||||
"EXE003",
|
||||
"EXE004",
|
||||
"EXE005",
|
||||
"F",
|
||||
"F4",
|
||||
"F40",
|
||||
@@ -1421,15 +1484,6 @@
|
||||
"I00",
|
||||
"I001",
|
||||
"I002",
|
||||
"I2",
|
||||
"I25",
|
||||
"I252",
|
||||
"IC",
|
||||
"IC0",
|
||||
"IC001",
|
||||
"IC002",
|
||||
"IC003",
|
||||
"IC004",
|
||||
"ICN",
|
||||
"ICN0",
|
||||
"ICN00",
|
||||
@@ -1444,9 +1498,6 @@
|
||||
"ISC001",
|
||||
"ISC002",
|
||||
"ISC003",
|
||||
"M",
|
||||
"M0",
|
||||
"M001",
|
||||
"N",
|
||||
"N8",
|
||||
"N80",
|
||||
@@ -1484,23 +1535,6 @@
|
||||
"PD9",
|
||||
"PD90",
|
||||
"PD901",
|
||||
"PDV",
|
||||
"PDV0",
|
||||
"PDV002",
|
||||
"PDV003",
|
||||
"PDV004",
|
||||
"PDV007",
|
||||
"PDV008",
|
||||
"PDV009",
|
||||
"PDV01",
|
||||
"PDV010",
|
||||
"PDV011",
|
||||
"PDV012",
|
||||
"PDV013",
|
||||
"PDV015",
|
||||
"PDV9",
|
||||
"PDV90",
|
||||
"PDV901",
|
||||
"PGH",
|
||||
"PGH0",
|
||||
"PGH00",
|
||||
@@ -1516,7 +1550,10 @@
|
||||
"PIE796",
|
||||
"PIE8",
|
||||
"PIE80",
|
||||
"PIE800",
|
||||
"PIE804",
|
||||
"PIE807",
|
||||
"PL",
|
||||
"PLC",
|
||||
"PLC0",
|
||||
"PLC04",
|
||||
@@ -1595,6 +1632,36 @@
|
||||
"PT024",
|
||||
"PT025",
|
||||
"PT026",
|
||||
"PTH",
|
||||
"PTH1",
|
||||
"PTH10",
|
||||
"PTH100",
|
||||
"PTH101",
|
||||
"PTH102",
|
||||
"PTH103",
|
||||
"PTH104",
|
||||
"PTH105",
|
||||
"PTH106",
|
||||
"PTH107",
|
||||
"PTH108",
|
||||
"PTH109",
|
||||
"PTH11",
|
||||
"PTH110",
|
||||
"PTH111",
|
||||
"PTH112",
|
||||
"PTH113",
|
||||
"PTH114",
|
||||
"PTH115",
|
||||
"PTH116",
|
||||
"PTH117",
|
||||
"PTH118",
|
||||
"PTH119",
|
||||
"PTH12",
|
||||
"PTH120",
|
||||
"PTH121",
|
||||
"PTH122",
|
||||
"PTH123",
|
||||
"PTH124",
|
||||
"Q",
|
||||
"Q0",
|
||||
"Q00",
|
||||
@@ -1602,17 +1669,6 @@
|
||||
"Q001",
|
||||
"Q002",
|
||||
"Q003",
|
||||
"R",
|
||||
"R5",
|
||||
"R50",
|
||||
"R501",
|
||||
"R502",
|
||||
"R503",
|
||||
"R504",
|
||||
"R505",
|
||||
"R506",
|
||||
"R507",
|
||||
"R508",
|
||||
"RET",
|
||||
"RET5",
|
||||
"RET50",
|
||||
@@ -1657,6 +1713,9 @@
|
||||
"S506",
|
||||
"S508",
|
||||
"S509",
|
||||
"S6",
|
||||
"S61",
|
||||
"S612",
|
||||
"S7",
|
||||
"S70",
|
||||
"S701",
|
||||
@@ -1710,27 +1769,22 @@
|
||||
"TID25",
|
||||
"TID251",
|
||||
"TID252",
|
||||
"U",
|
||||
"U0",
|
||||
"U00",
|
||||
"U001",
|
||||
"U003",
|
||||
"U004",
|
||||
"U005",
|
||||
"U006",
|
||||
"U007",
|
||||
"U008",
|
||||
"U009",
|
||||
"U01",
|
||||
"U010",
|
||||
"U011",
|
||||
"U012",
|
||||
"U013",
|
||||
"U014",
|
||||
"U015",
|
||||
"U016",
|
||||
"U017",
|
||||
"U019",
|
||||
"TRY",
|
||||
"TRY0",
|
||||
"TRY00",
|
||||
"TRY004",
|
||||
"TRY2",
|
||||
"TRY20",
|
||||
"TRY200",
|
||||
"TRY201",
|
||||
"TRY3",
|
||||
"TRY30",
|
||||
"TRY300",
|
||||
"TRY301",
|
||||
"TYP",
|
||||
"TYP0",
|
||||
"TYP00",
|
||||
"TYP005",
|
||||
"UP",
|
||||
"UP0",
|
||||
"UP00",
|
||||
@@ -1766,8 +1820,10 @@
|
||||
"UP029",
|
||||
"UP03",
|
||||
"UP030",
|
||||
"UP031",
|
||||
"UP032",
|
||||
"UP033",
|
||||
"UP034",
|
||||
"W",
|
||||
"W2",
|
||||
"W29",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -35,10 +35,19 @@ fn content_dir() -> &'static Path {
|
||||
Path::new("content")
|
||||
}
|
||||
|
||||
fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: flags::Autofix) -> u64 {
|
||||
fn cache_key<P: AsRef<Path>>(
|
||||
path: P,
|
||||
package: Option<&P>,
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
CARGO_PKG_VERSION.hash(&mut hasher);
|
||||
path.as_ref().absolutize().unwrap().hash(&mut hasher);
|
||||
package
|
||||
.as_ref()
|
||||
.map(|path| path.as_ref().absolutize().unwrap())
|
||||
.hash(&mut hasher);
|
||||
settings.hash(&mut hasher);
|
||||
autofix.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
@@ -79,13 +88,14 @@ fn read_sync(cache_dir: &Path, key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
/// Get a value from the cache.
|
||||
pub fn get<P: AsRef<Path>>(
|
||||
path: P,
|
||||
package: Option<&P>,
|
||||
metadata: &fs::Metadata,
|
||||
settings: &AllSettings,
|
||||
autofix: flags::Autofix,
|
||||
) -> Option<Vec<Message>> {
|
||||
let encoded = read_sync(
|
||||
&settings.cli.cache_dir,
|
||||
cache_key(path, &settings.lib, autofix),
|
||||
cache_key(path, package, &settings.lib, autofix),
|
||||
)
|
||||
.ok()?;
|
||||
let (mtime, messages) = match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
@@ -107,6 +117,7 @@ pub fn get<P: AsRef<Path>>(
|
||||
/// Set a value in the cache.
|
||||
pub fn set<P: AsRef<Path>>(
|
||||
path: P,
|
||||
package: Option<&P>,
|
||||
metadata: &fs::Metadata,
|
||||
settings: &AllSettings,
|
||||
autofix: flags::Autofix,
|
||||
@@ -120,7 +131,7 @@ pub fn set<P: AsRef<Path>>(
|
||||
};
|
||||
if let Err(e) = write_sync(
|
||||
&settings.cli.cache_dir,
|
||||
cache_key(path, &settings.lib, autofix),
|
||||
cache_key(path, package, &settings.lib, autofix),
|
||||
&bincode::serialize(&check_result).unwrap(),
|
||||
) {
|
||||
error!("Failed to write to cache: {e:?}");
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{command, Parser};
|
||||
use regex::Regex;
|
||||
use ruff::fs;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::registry::{Rule, RuleCodePrefix};
|
||||
use ruff::registry::Rule;
|
||||
use ruff::resolver::ConfigProcessor;
|
||||
use ruff::settings::types::{
|
||||
FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat,
|
||||
};
|
||||
use ruff::RuleSelector;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -66,18 +66,18 @@ pub struct Cli {
|
||||
/// Comma-separated list of rule codes to enable (or ALL, to enable all
|
||||
/// rules).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub select: Option<Vec<RuleCodePrefix>>,
|
||||
pub select: Option<Vec<RuleSelector>>,
|
||||
/// Like --select, but adds additional rule codes on top of the selected
|
||||
/// ones.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub extend_select: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
/// Comma-separated list of rule codes to disable.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub ignore: Option<Vec<RuleSelector>>,
|
||||
/// Like --ignore, but adds additional rule codes on top of the ignored
|
||||
/// ones.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub extend_ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
/// List of paths, used to omit files and/or directories from analysis.
|
||||
#[arg(long, value_delimiter = ',', value_name = "FILE_PATTERN")]
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
@@ -88,11 +88,11 @@ pub struct Cli {
|
||||
/// List of rule codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub fixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
/// List of rule codes to treat as ineligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub unfixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub unfixable: Option<Vec<RuleSelector>>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||
@@ -324,17 +324,17 @@ pub struct Overrides {
|
||||
pub dummy_variable_rgx: Option<Regex>,
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
pub extend_exclude: Option<Vec<FilePattern>>,
|
||||
pub extend_ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_select: Option<Vec<RuleCodePrefix>>,
|
||||
pub fixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
pub ignore: Option<Vec<RuleSelector>>,
|
||||
pub line_length: Option<usize>,
|
||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||
pub respect_gitignore: Option<bool>,
|
||||
pub select: Option<Vec<RuleCodePrefix>>,
|
||||
pub select: Option<Vec<RuleSelector>>,
|
||||
pub show_source: Option<bool>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub unfixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub unfixable: Option<Vec<RuleSelector>>,
|
||||
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
pub fix: Option<bool>,
|
||||
@@ -435,7 +435,7 @@ pub fn extract_log_level(cli: &Arguments) -> LogLevel {
|
||||
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgnore> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleCodePrefix>> = FxHashMap::default();
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleSelector>> = FxHashMap::default();
|
||||
for pair in pairs {
|
||||
per_file_ignores
|
||||
.entry(pair.pattern)
|
||||
@@ -444,9 +444,6 @@ pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgn
|
||||
}
|
||||
per_file_ignores
|
||||
.into_iter()
|
||||
.map(|(pattern, prefixes)| {
|
||||
let absolute = fs::normalize_path(Path::new(&pattern));
|
||||
PerFileIgnore::new(pattern, absolute, &prefixes)
|
||||
})
|
||||
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, &prefixes, None))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use ruff::cache::CACHE_DIR_NAME;
|
||||
use ruff::linter::add_noqa_to_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
use ruff::registry::Rule;
|
||||
use ruff::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff::resolver::{FileDiscovery, PyprojectDiscovery};
|
||||
use ruff::settings::flags;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
@@ -285,16 +285,17 @@ pub fn show_files(
|
||||
#[derive(Serialize)]
|
||||
struct Explanation<'a> {
|
||||
code: &'a str,
|
||||
origin: &'a str,
|
||||
linter: &'a str,
|
||||
summary: &'a str,
|
||||
}
|
||||
|
||||
/// Explain a `Rule` to the user.
|
||||
pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||
match format {
|
||||
SerializationFormat::Text | SerializationFormat::Grouped => {
|
||||
println!("{}\n", rule.as_ref());
|
||||
println!("Code: {} ({})\n", rule.code(), rule.origin().name());
|
||||
println!("Code: {} ({})\n", rule.code(), linter.name());
|
||||
|
||||
if let Some(autofix) = rule.autofixable() {
|
||||
println!(
|
||||
@@ -315,7 +316,7 @@ pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
code: rule.code(),
|
||||
origin: rule.origin().name(),
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
})?
|
||||
);
|
||||
|
||||
@@ -55,7 +55,9 @@ pub fn lint_path(
|
||||
&& matches!(autofix, fix::FixMode::None | fix::FixMode::Generate)
|
||||
{
|
||||
let metadata = path.metadata()?;
|
||||
if let Some(messages) = cache::get(path, &metadata, settings, autofix.into()) {
|
||||
if let Some(messages) =
|
||||
cache::get(path, package.as_ref(), &metadata, settings, autofix.into())
|
||||
{
|
||||
debug!("Cache hit for: {}", path.to_string_lossy());
|
||||
return Ok(Diagnostics::new(messages));
|
||||
}
|
||||
@@ -92,7 +94,14 @@ pub fn lint_path(
|
||||
|
||||
// Re-populate the cache.
|
||||
if let Some(metadata) = metadata {
|
||||
cache::set(path, &metadata, settings, autofix.into(), &messages);
|
||||
cache::set(
|
||||
path,
|
||||
package.as_ref(),
|
||||
&metadata,
|
||||
settings,
|
||||
autofix.into(),
|
||||
&messages,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Diagnostics { messages, fixed })
|
||||
|
||||
@@ -10,11 +10,8 @@ use std::{fs, process, str};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use assert_cmd::Command;
|
||||
use itertools::Itertools;
|
||||
use log::info;
|
||||
use ruff::logging::{set_up_logging, LogLevel};
|
||||
use ruff::registry::RuleOrigin;
|
||||
use strum::IntoEnumIterator;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// Handles `blackd` process and allows submitting code to it for formatting.
|
||||
@@ -175,13 +172,6 @@ fn test_ruff_black_compatibility() -> Result<()> {
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
let codes = RuleOrigin::iter()
|
||||
// Exclude ruff codes, specifically RUF100, because it causes differences that are not a
|
||||
// problem. Ruff would add a `# noqa: W292` after the first run, black introduces a
|
||||
// newline, and ruff removes the `# noqa: W292` again.
|
||||
.filter(|origin| *origin != RuleOrigin::Ruff)
|
||||
.map(|origin| origin.prefixes().as_list(","))
|
||||
.join(",");
|
||||
let ruff_args = [
|
||||
"-",
|
||||
"--silent",
|
||||
@@ -189,8 +179,11 @@ fn test_ruff_black_compatibility() -> Result<()> {
|
||||
"--fix",
|
||||
"--line-length",
|
||||
"88",
|
||||
"--select",
|
||||
&codes,
|
||||
"--select ALL",
|
||||
// Exclude ruff codes, specifically RUF100, because it causes differences that are not a
|
||||
// problem. Ruff would add a `# noqa: W292` after the first run, black introduces a
|
||||
// newline, and ruff removes the `# noqa: W292` again.
|
||||
"--ignore RUF",
|
||||
];
|
||||
|
||||
for entry in paths {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,9 +11,9 @@ libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a87
|
||||
once_cell = { version = "1.16.0" }
|
||||
ruff = { path = ".." }
|
||||
ruff_cli = { path = "../ruff_cli" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde_json = {version="1.0.91"}
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use ruff::registry::{Prefixes, RuleCodePrefix, RuleOrigin};
|
||||
use ruff::registry::{Linter, LinterCategory, Rule, RuleNamespace};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
@@ -20,12 +20,12 @@ pub struct Cli {
|
||||
pub(crate) dry_run: bool,
|
||||
}
|
||||
|
||||
fn generate_table(table_out: &mut String, prefix: &RuleCodePrefix) {
|
||||
fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>) {
|
||||
table_out.push_str("| Code | Name | Message | Fix |");
|
||||
table_out.push('\n');
|
||||
table_out.push_str("| ---- | ---- | ------- | --- |");
|
||||
table_out.push('\n');
|
||||
for rule in prefix.codes() {
|
||||
for rule in rules {
|
||||
let fix_token = match rule.autofixable() {
|
||||
None => "",
|
||||
Some(_) => "🛠",
|
||||
@@ -47,22 +47,21 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
// Generate the table string.
|
||||
let mut table_out = String::new();
|
||||
let mut toc_out = String::new();
|
||||
for origin in RuleOrigin::iter() {
|
||||
let prefixes = origin.prefixes();
|
||||
let codes_csv: String = prefixes.as_list(", ");
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", origin.name()));
|
||||
for linter in Linter::iter() {
|
||||
let codes_csv: String = linter.prefixes().join(", ");
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", linter.name()));
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
|
||||
toc_out.push_str(&format!(
|
||||
" 1. [{} ({})](#{}-{})\n",
|
||||
origin.name(),
|
||||
linter.name(),
|
||||
codes_csv,
|
||||
origin.name().to_lowercase().replace(' ', "-"),
|
||||
linter.name().to_lowercase().replace(' ', "-"),
|
||||
codes_csv.to_lowercase().replace(',', "-").replace(' ', "")
|
||||
));
|
||||
|
||||
if let Some(url) = origin.url() {
|
||||
if let Some(url) = linter.url() {
|
||||
let host = url
|
||||
.trim_start_matches("https://")
|
||||
.split('/')
|
||||
@@ -70,7 +69,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.unwrap();
|
||||
table_out.push_str(&format!(
|
||||
"For more, see [{}]({}) on {}.",
|
||||
origin.name(),
|
||||
linter.name(),
|
||||
url,
|
||||
match host {
|
||||
"pypi.org" => "PyPI",
|
||||
@@ -78,7 +77,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
host => panic!(
|
||||
"unexpected host in URL of {}, expected pypi.org or github.com but found \
|
||||
{host}",
|
||||
origin.name()
|
||||
linter.name()
|
||||
),
|
||||
}
|
||||
));
|
||||
@@ -86,15 +85,14 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
table_out.push('\n');
|
||||
}
|
||||
|
||||
match prefixes {
|
||||
Prefixes::Single(prefix) => generate_table(&mut table_out, &prefix),
|
||||
Prefixes::Multiple(entries) => {
|
||||
for (prefix, category) in entries {
|
||||
table_out.push_str(&format!("#### {category} ({})", prefix.as_ref()));
|
||||
table_out.push('\n');
|
||||
generate_table(&mut table_out, &prefix);
|
||||
}
|
||||
if let Some(categories) = linter.categories() {
|
||||
for LinterCategory(prefix, name, selector) in categories {
|
||||
table_out.push_str(&format!("#### {name} ({prefix})"));
|
||||
table_out.push('\n');
|
||||
generate_table(&mut table_out, selector);
|
||||
}
|
||||
} else {
|
||||
generate_table(&mut table_out, &linter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.227"
|
||||
version = "0.0.233"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -10,7 +10,6 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
let mut diagkind_variants = quote!();
|
||||
let mut rule_message_formats_match_arms = quote!();
|
||||
let mut rule_autofixable_match_arms = quote!();
|
||||
let mut rule_origin_match_arms = quote!();
|
||||
let mut rule_code_match_arms = quote!();
|
||||
let mut rule_from_code_match_arms = quote!();
|
||||
let mut diagkind_code_match_arms = quote!();
|
||||
@@ -29,8 +28,6 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
rule_message_formats_match_arms
|
||||
.extend(quote! {Self::#name => <#path as Violation>::message_formats(),});
|
||||
rule_autofixable_match_arms.extend(quote! {Self::#name => <#path as Violation>::AUTOFIX,});
|
||||
let origin = get_origin(code);
|
||||
rule_origin_match_arms.extend(quote! {Self::#name => RuleOrigin::#origin,});
|
||||
rule_code_match_arms.extend(quote! {Self::#name => #code_str,});
|
||||
rule_from_code_match_arms.extend(quote! {#code_str => Ok(&Rule::#name), });
|
||||
diagkind_code_match_arms.extend(quote! {Self::#name(..) => &Rule::#name, });
|
||||
@@ -95,10 +92,6 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
match self { #rule_autofixable_match_arms }
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> RuleOrigin {
|
||||
match self { #rule_origin_match_arms }
|
||||
}
|
||||
|
||||
pub fn code(&self) -> &'static str {
|
||||
match self { #rule_code_match_arms }
|
||||
}
|
||||
@@ -140,19 +133,6 @@ pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_origin(ident: &Ident) -> Ident {
|
||||
let ident = ident.to_string();
|
||||
let mut iter = crate::prefixes::PREFIX_TO_ORIGIN.iter();
|
||||
let origin = loop {
|
||||
let (prefix, origin) = iter
|
||||
.next()
|
||||
.unwrap_or_else(|| panic!("code doesn't start with any recognized prefix: {ident}"));
|
||||
if ident.starts_with(prefix) {
|
||||
break origin;
|
||||
}
|
||||
};
|
||||
Ident::new(origin, Span::call_site())
|
||||
}
|
||||
pub struct Mapping {
|
||||
entries: Vec<(Ident, Path, Ident)>,
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ use syn::{parse_macro_input, DeriveInput, ItemFn};
|
||||
mod config;
|
||||
mod define_rule_mapping;
|
||||
mod derive_message_formats;
|
||||
mod prefixes;
|
||||
mod rule_code_prefix;
|
||||
mod rule_namespace;
|
||||
|
||||
#[proc_macro_derive(ConfigurationOptions, attributes(option, doc, option_group))]
|
||||
pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
@@ -37,6 +37,15 @@ pub fn define_rule_mapping(item: proc_macro::TokenStream) -> proc_macro::TokenSt
|
||||
define_rule_mapping::define_rule_mapping(&mapping).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(RuleNamespace, attributes(prefix))]
|
||||
pub fn derive_rule_namespace(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
rule_namespace::derive_impl(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn derive_message_formats(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// Longer prefixes should come first so that you can find an origin for a code
|
||||
// by simply picking the first entry that starts with the given prefix.
|
||||
|
||||
pub const PREFIX_TO_ORIGIN: &[(&str, &str)] = &[
|
||||
("ANN", "Flake8Annotations"),
|
||||
("ARG", "Flake8UnusedArguments"),
|
||||
("A", "Flake8Builtins"),
|
||||
("BLE", "Flake8BlindExcept"),
|
||||
("B", "Flake8Bugbear"),
|
||||
("C4", "Flake8Comprehensions"),
|
||||
("C9", "McCabe"),
|
||||
("COM", "Flake8Commas"),
|
||||
("DTZ", "Flake8Datetimez"),
|
||||
("D", "Pydocstyle"),
|
||||
("ERA", "Eradicate"),
|
||||
("EM", "Flake8ErrMsg"),
|
||||
("E", "Pycodestyle"),
|
||||
("FBT", "Flake8BooleanTrap"),
|
||||
("F", "Pyflakes"),
|
||||
("ICN", "Flake8ImportConventions"),
|
||||
("ISC", "Flake8ImplicitStrConcat"),
|
||||
("I", "Isort"),
|
||||
("N", "PEP8Naming"),
|
||||
("PD", "PandasVet"),
|
||||
("PGH", "PygrepHooks"),
|
||||
("PL", "Pylint"),
|
||||
("PT", "Flake8PytestStyle"),
|
||||
("Q", "Flake8Quotes"),
|
||||
("RET", "Flake8Return"),
|
||||
("SIM", "Flake8Simplify"),
|
||||
("S", "Flake8Bandit"),
|
||||
("T10", "Flake8Debugger"),
|
||||
("T20", "Flake8Print"),
|
||||
("TID", "Flake8TidyImports"),
|
||||
("UP", "Pyupgrade"),
|
||||
("W", "Pycodestyle"),
|
||||
("YTT", "Flake82020"),
|
||||
("PIE", "Flake8Pie"),
|
||||
("RUF", "Ruff"),
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PREFIX_TO_ORIGIN;
|
||||
|
||||
#[test]
|
||||
fn order() {
|
||||
for (idx, (prefix, _)) in PREFIX_TO_ORIGIN.iter().enumerate() {
|
||||
for (prior_prefix, _) in PREFIX_TO_ORIGIN[..idx].iter() {
|
||||
assert!(!prefix.starts_with(prior_prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +1,9 @@
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
const ALL: &str = "ALL";
|
||||
|
||||
/// A hash map from deprecated `RuleCodePrefix` to latest
|
||||
/// `RuleCodePrefix`.
|
||||
pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
HashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U001", "UP001"),
|
||||
("U003", "UP003"),
|
||||
("U004", "UP004"),
|
||||
("U005", "UP005"),
|
||||
("U006", "UP006"),
|
||||
("U007", "UP007"),
|
||||
("U008", "UP008"),
|
||||
("U009", "UP009"),
|
||||
("U010", "UP010"),
|
||||
("U011", "UP011"),
|
||||
("U012", "UP012"),
|
||||
("U013", "UP013"),
|
||||
("U014", "UP014"),
|
||||
("U015", "UP015"),
|
||||
("U016", "UP016"),
|
||||
("U017", "UP017"),
|
||||
("U019", "UP019"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", "TID252"),
|
||||
("M001", "RUF100"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("PDV002", "PD002"),
|
||||
("PDV003", "PD003"),
|
||||
("PDV004", "PD004"),
|
||||
("PDV007", "PD007"),
|
||||
("PDV008", "PD008"),
|
||||
("PDV009", "PD009"),
|
||||
("PDV010", "PD010"),
|
||||
("PDV011", "PD011"),
|
||||
("PDV012", "PD012"),
|
||||
("PDV013", "PD013"),
|
||||
("PDV015", "PD015"),
|
||||
("PDV901", "PD901"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("R501", "RET501"),
|
||||
("R502", "RET502"),
|
||||
("R503", "RET503"),
|
||||
("R504", "RET504"),
|
||||
("R505", "RET505"),
|
||||
("R506", "RET506"),
|
||||
("R507", "RET507"),
|
||||
("R508", "RET508"),
|
||||
("IC001", "ICN001"),
|
||||
("IC002", "ICN001"),
|
||||
("IC003", "ICN001"),
|
||||
("IC004", "ICN001"),
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U", "UP"),
|
||||
("U0", "UP0"),
|
||||
("U00", "UP00"),
|
||||
("U01", "UP01"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I2", "TID2"),
|
||||
("I25", "TID25"),
|
||||
("M", "RUF100"),
|
||||
("M0", "RUF100"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("PDV", "PD"),
|
||||
("PDV0", "PD0"),
|
||||
("PDV01", "PD01"),
|
||||
("PDV9", "PD9"),
|
||||
("PDV90", "PD90"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("R", "RET"),
|
||||
("R5", "RET5"),
|
||||
("R50", "RET50"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("IC", "ICN"),
|
||||
("IC0", "ICN0"),
|
||||
])
|
||||
});
|
||||
|
||||
pub fn expand<'a>(
|
||||
rule_type: &Ident,
|
||||
prefix_ident: &Ident,
|
||||
@@ -91,9 +11,12 @@ pub fn expand<'a>(
|
||||
variant_name: impl Fn(&str) -> &'a Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
// Build up a map from prefix to matching RuleCodes.
|
||||
let mut prefix_to_codes: BTreeMap<Ident, BTreeSet<String>> = BTreeMap::default();
|
||||
let mut prefix_to_codes: BTreeMap<String, BTreeSet<String>> = BTreeMap::default();
|
||||
|
||||
let mut all_codes = BTreeSet::new();
|
||||
let mut pl_codes = BTreeSet::new();
|
||||
|
||||
for variant in variants {
|
||||
let span = variant.span();
|
||||
let code_str = variant.to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
@@ -103,28 +26,20 @@ pub fn expand<'a>(
|
||||
for i in 0..=code_suffix_len {
|
||||
let prefix = code_str[..code_prefix_len + i].to_string();
|
||||
prefix_to_codes
|
||||
.entry(Ident::new(&prefix, span))
|
||||
.entry(prefix)
|
||||
.or_default()
|
||||
.insert(code_str.clone());
|
||||
}
|
||||
prefix_to_codes
|
||||
.entry(Ident::new(ALL, span))
|
||||
.or_default()
|
||||
.insert(code_str.clone());
|
||||
if code_str.starts_with("PL") {
|
||||
pl_codes.insert(code_str.to_string());
|
||||
}
|
||||
all_codes.insert(code_str);
|
||||
}
|
||||
|
||||
// Add any prefix aliases (e.g., "U" to "UP").
|
||||
for (alias, rule_code) in PREFIX_REDIRECTS.iter() {
|
||||
prefix_to_codes.insert(
|
||||
Ident::new(alias, Span::call_site()),
|
||||
prefix_to_codes
|
||||
.get(&Ident::new(rule_code, Span::call_site()))
|
||||
.unwrap_or_else(|| panic!("Unknown RuleCode: {alias:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
prefix_to_codes.insert("PL".to_string(), pl_codes);
|
||||
|
||||
let prefix_variants = prefix_to_codes.keys().map(|prefix| {
|
||||
let prefix = Ident::new(prefix, Span::call_site());
|
||||
quote! {
|
||||
#prefix
|
||||
}
|
||||
@@ -132,25 +47,9 @@ pub fn expand<'a>(
|
||||
|
||||
let prefix_impl = generate_impls(rule_type, prefix_ident, &prefix_to_codes, variant_name);
|
||||
|
||||
let prefix_redirects = PREFIX_REDIRECTS.iter().map(|(alias, rule_code)| {
|
||||
let code = Ident::new(rule_code, Span::call_site());
|
||||
quote! {
|
||||
(#alias, #prefix_ident::#code)
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SuffixLength {
|
||||
None,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
::strum_macros::EnumIter,
|
||||
::strum_macros::EnumString,
|
||||
::strum_macros::AsRefStr,
|
||||
Debug,
|
||||
@@ -159,105 +58,63 @@ pub fn expand<'a>(
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Clone,
|
||||
Hash,
|
||||
::serde::Serialize,
|
||||
::serde::Deserialize,
|
||||
::schemars::JsonSchema,
|
||||
)]
|
||||
pub enum #prefix_ident {
|
||||
#(#prefix_variants,)*
|
||||
}
|
||||
|
||||
#prefix_impl
|
||||
|
||||
/// A hash map from deprecated `RuleCodePrefix` to latest `RuleCodePrefix`.
|
||||
pub static PREFIX_REDIRECTS: ::once_cell::sync::Lazy<::rustc_hash::FxHashMap<&'static str, #prefix_ident>> = ::once_cell::sync::Lazy::new(|| {
|
||||
::rustc_hash::FxHashMap::from_iter([
|
||||
#(#prefix_redirects),*
|
||||
])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_impls<'a>(
|
||||
rule_type: &Ident,
|
||||
prefix_ident: &Ident,
|
||||
prefix_to_codes: &BTreeMap<Ident, BTreeSet<String>>,
|
||||
prefix_to_codes: &BTreeMap<String, BTreeSet<String>>,
|
||||
variant_name: impl Fn(&str) -> &'a Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let codes_match_arms = prefix_to_codes.iter().map(|(prefix, codes)| {
|
||||
let into_iter_match_arms = prefix_to_codes.iter().map(|(prefix_str, codes)| {
|
||||
let codes = codes.iter().map(|code| {
|
||||
let rule_variant = variant_name(code);
|
||||
quote! {
|
||||
#rule_type::#rule_variant
|
||||
}
|
||||
});
|
||||
let prefix_str = prefix.to_string();
|
||||
if let Some(target) = PREFIX_REDIRECTS.get(prefix_str.as_str()) {
|
||||
quote! {
|
||||
#prefix_ident::#prefix => {
|
||||
crate::warn_user_once!(
|
||||
"`{}` has been remapped to `{}`", #prefix_str, #target
|
||||
);
|
||||
vec![#(#codes),*]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#prefix_ident::#prefix => vec![#(#codes),*],
|
||||
}
|
||||
let prefix = Ident::new(prefix_str, Span::call_site());
|
||||
|
||||
quote! {
|
||||
#prefix_ident::#prefix => vec![#(#codes),*].into_iter(),
|
||||
}
|
||||
});
|
||||
|
||||
let specificity_match_arms = prefix_to_codes.keys().map(|prefix| {
|
||||
if *prefix == ALL {
|
||||
quote! {
|
||||
#prefix_ident::#prefix => SuffixLength::None,
|
||||
}
|
||||
} else {
|
||||
let num_numeric = prefix
|
||||
.to_string()
|
||||
.chars()
|
||||
.filter(|char| char.is_numeric())
|
||||
.count();
|
||||
let suffix_len = match num_numeric {
|
||||
0 => quote! { SuffixLength::Zero },
|
||||
1 => quote! { SuffixLength::One },
|
||||
2 => quote! { SuffixLength::Two },
|
||||
3 => quote! { SuffixLength::Three },
|
||||
4 => quote! { SuffixLength::Four },
|
||||
_ => panic!("Invalid prefix: {prefix}"),
|
||||
};
|
||||
quote! {
|
||||
#prefix_ident::#prefix => #suffix_len,
|
||||
}
|
||||
let specificity_match_arms = prefix_to_codes.keys().map(|prefix_str| {
|
||||
let prefix = Ident::new(prefix_str, Span::call_site());
|
||||
let mut num_numeric = prefix_str.chars().filter(|char| char.is_numeric()).count();
|
||||
if prefix_str != "PL" && prefix_str.starts_with("PL") {
|
||||
num_numeric += 1;
|
||||
}
|
||||
});
|
||||
|
||||
let categories = prefix_to_codes.keys().map(|prefix| {
|
||||
let prefix_str = prefix.to_string();
|
||||
if prefix_str.chars().all(char::is_alphabetic)
|
||||
&& !PREFIX_REDIRECTS.contains_key(&prefix_str.as_str())
|
||||
{
|
||||
quote! {
|
||||
#prefix_ident::#prefix,
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
let suffix_len = match num_numeric {
|
||||
0 => quote! { Specificity::Linter },
|
||||
1 => quote! { Specificity::Code1Char },
|
||||
2 => quote! { Specificity::Code2Chars },
|
||||
3 => quote! { Specificity::Code3Chars },
|
||||
4 => quote! { Specificity::Code4Chars },
|
||||
5 => quote! { Specificity::Code5Chars },
|
||||
_ => panic!("Invalid prefix: {prefix}"),
|
||||
};
|
||||
quote! {
|
||||
#prefix_ident::#prefix => #suffix_len,
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
impl #prefix_ident {
|
||||
pub fn codes(&self) -> Vec<#rule_type> {
|
||||
use colored::Colorize;
|
||||
pub(crate) fn specificity(&self) -> crate::rule_selector::Specificity {
|
||||
use crate::rule_selector::Specificity;
|
||||
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
#(#codes_match_arms)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn specificity(&self) -> SuffixLength {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
#(#specificity_match_arms)*
|
||||
@@ -265,6 +122,16 @@ fn generate_impls<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
pub const CATEGORIES: &[#prefix_ident] = &[#(#categories)*];
|
||||
impl IntoIterator for &#prefix_ident {
|
||||
type Item = #rule_type;
|
||||
type IntoIter = ::std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
#(#into_iter_match_arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
153
ruff_macros/src/rule_namespace.rs
Normal file
153
ruff_macros/src/rule_namespace.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Attribute, Data, DataEnum, DeriveInput, Error, Lit, Meta, MetaNameValue};
|
||||
|
||||
pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let DeriveInput { ident, data: Data::Enum(DataEnum {
|
||||
variants, ..
|
||||
}), .. } = input else {
|
||||
return Err(Error::new(input.ident.span(), "only named fields are supported"));
|
||||
};
|
||||
|
||||
let mut parsed = Vec::new();
|
||||
|
||||
let mut prefix_match_arms = quote!();
|
||||
let mut name_match_arms = quote!(Self::Ruff => "Ruff-specific rules",);
|
||||
let mut url_match_arms = quote!(Self::Ruff => None,);
|
||||
|
||||
let mut all_prefixes = HashSet::new();
|
||||
|
||||
for variant in variants {
|
||||
let prefixes: Result<Vec<_>, _> = variant
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|a| a.path.is_ident("prefix"))
|
||||
.map(|attr| {
|
||||
let Ok(Meta::NameValue(MetaNameValue{lit: Lit::Str(lit), ..})) = attr.parse_meta() else {
|
||||
return Err(Error::new(attr.span(), r#"expected attribute to be in the form of [#prefix = "..."]"#));
|
||||
};
|
||||
let str = lit.value();
|
||||
match str.chars().next() {
|
||||
None => return Err(Error::new(lit.span(), "expected prefix string to be non-empty")),
|
||||
Some(_) => {},
|
||||
}
|
||||
if !all_prefixes.insert(str.clone()) {
|
||||
return Err(Error::new(lit.span(), "prefix has already been defined before"));
|
||||
}
|
||||
Ok(str)
|
||||
})
|
||||
.collect();
|
||||
let prefixes = prefixes?;
|
||||
|
||||
if prefixes.is_empty() {
|
||||
return Err(Error::new(
|
||||
variant.span(),
|
||||
r#"Missing #[prefix = "..."] attribute"#,
|
||||
));
|
||||
}
|
||||
|
||||
let Some(doc_attr) = variant.attrs.iter().find(|a| a.path.is_ident("doc")) else {
|
||||
return Err(Error::new(variant.span(), r#"expected a doc comment"#))
|
||||
};
|
||||
|
||||
let variant_ident = variant.ident;
|
||||
|
||||
if variant_ident != "Ruff" {
|
||||
let (name, url) = parse_doc_attr(doc_attr)?;
|
||||
name_match_arms.extend(quote! {Self::#variant_ident => #name,});
|
||||
url_match_arms.extend(quote! {Self::#variant_ident => Some(#url),});
|
||||
}
|
||||
|
||||
for lit in &prefixes {
|
||||
parsed.push((lit.clone(), variant_ident.clone()));
|
||||
}
|
||||
|
||||
prefix_match_arms.extend(quote! {
|
||||
Self::#variant_ident => &[#(#prefixes),*],
|
||||
});
|
||||
}
|
||||
|
||||
parsed.sort_by_key(|(prefix, _)| prefix.len());
|
||||
|
||||
let mut if_statements = quote!();
|
||||
let mut into_iter_match_arms = quote!();
|
||||
|
||||
for (prefix, field) in parsed {
|
||||
if_statements.extend(quote! {if let Some(rest) = code.strip_prefix(#prefix) {
|
||||
return Some((#ident::#field, rest));
|
||||
}});
|
||||
|
||||
let prefix_ident = Ident::new(&prefix, Span::call_site());
|
||||
|
||||
if field != "Pycodestyle" {
|
||||
into_iter_match_arms.extend(quote! {
|
||||
#ident::#field => RuleCodePrefix::#prefix_ident.into_iter(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
into_iter_match_arms.extend(quote! {
|
||||
#ident::Pycodestyle => {
|
||||
let rules: Vec<_> = (&RuleCodePrefix::E).into_iter().chain(&RuleCodePrefix::W).collect();
|
||||
rules.into_iter()
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
impl crate::registry::RuleNamespace for #ident {
|
||||
fn parse_code(code: &str) -> Option<(Self, &str)> {
|
||||
#if_statements
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
fn prefixes(&self) -> &'static [&'static str] {
|
||||
match self { #prefix_match_arms }
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
match self { #name_match_arms }
|
||||
}
|
||||
|
||||
fn url(&self) -> Option<&'static str> {
|
||||
match self { #url_match_arms }
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for &#ident {
|
||||
type Item = Rule;
|
||||
type IntoIter = ::std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
use colored::Colorize;
|
||||
|
||||
match self {
|
||||
#into_iter_match_arms
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses an attribute in the form of `#[doc = " [name](https://example.com/)"]`
|
||||
/// into a tuple of link label and URL.
|
||||
fn parse_doc_attr(doc_attr: &Attribute) -> syn::Result<(String, String)> {
|
||||
let Ok(Meta::NameValue(MetaNameValue{lit: Lit::Str(doc_lit), ..})) = doc_attr.parse_meta() else {
|
||||
return Err(Error::new(doc_attr.span(), r#"expected doc attribute to be in the form of #[doc = "..."]"#))
|
||||
};
|
||||
parse_markdown_link(doc_lit.value().trim())
|
||||
.map(|(name, url)| (name.to_string(), url.to_string()))
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
doc_lit.span(),
|
||||
r#"expected doc comment to be in the form of `/// [name](https://example.com/)`"#,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_markdown_link(link: &str) -> Option<(&str, &str)> {
|
||||
link.strip_prefix('[')?.strip_suffix(')')?.split_once("](")
|
||||
}
|
||||
18
scripts/_utils.py
Normal file
18
scripts/_utils.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
def dir_name(linter_name: str) -> str:
|
||||
return linter_name.replace("-", "_")
|
||||
|
||||
|
||||
def pascal_case(linter_name: str) -> str:
|
||||
"""Convert from snake-case to PascalCase."""
|
||||
return "".join(word.title() for word in linter_name.split("-"))
|
||||
|
||||
|
||||
def get_indent(line: str) -> str:
|
||||
return re.match(r"^\s*", line).group() # pyright: ignore[reportOptionalMemberAccess]
|
||||
95
scripts/add_plugin.py
Normal file → Executable file
95
scripts/add_plugin.py
Normal file → Executable file
@@ -5,37 +5,29 @@ Example usage:
|
||||
|
||||
python scripts/add_plugin.py \
|
||||
flake8-pie \
|
||||
--url https://pypi.org/project/flake8-pie/0.16.0/
|
||||
--url https://pypi.org/project/flake8-pie/
|
||||
--prefix PIE
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from _utils import ROOT_DIR, dir_name, get_indent, pascal_case
|
||||
|
||||
|
||||
def dir_name(plugin: str) -> str:
|
||||
return plugin.replace("-", "_")
|
||||
|
||||
|
||||
def pascal_case(plugin: str) -> str:
|
||||
return "".join(word.title() for word in plugin.split("-"))
|
||||
|
||||
|
||||
def main(*, plugin: str, url: str) -> None:
|
||||
def main(*, plugin: str, url: str, prefix_code: str) -> None:
|
||||
# Create the test fixture folder.
|
||||
os.makedirs(
|
||||
ROOT_DIR / "resources/test/fixtures" / dir_name(plugin),
|
||||
exist_ok=True,
|
||||
)
|
||||
|
||||
# Create the Rust module.
|
||||
rust_module = ROOT_DIR / "src/rules" / dir_name(plugin)
|
||||
os.makedirs(rust_module, exist_ok=True)
|
||||
with open(rust_module / "rules.rs", "w+") as fp:
|
||||
fp.write("use crate::checkers::ast::Checker;\n")
|
||||
with open(rust_module / "mod.rs", "w+") as fp:
|
||||
# Create the Plugin rules module.
|
||||
plugin_dir = ROOT_DIR / "src/rules" / dir_name(plugin)
|
||||
plugin_dir.mkdir(exist_ok=True)
|
||||
|
||||
with (plugin_dir / "mod.rs").open("w+") as fp:
|
||||
fp.write(f"//! Rules from [{plugin}]({url}).\n")
|
||||
fp.write("pub(crate) mod rules;\n")
|
||||
fp.write("\n")
|
||||
fp.write(
|
||||
@@ -67,57 +59,37 @@ mod tests {
|
||||
% dir_name(plugin)
|
||||
)
|
||||
|
||||
# Create a subdirectory for rules and create a `mod.rs` placeholder
|
||||
rules_dir = plugin_dir / "rules"
|
||||
rules_dir.mkdir(exist_ok=True)
|
||||
|
||||
with (rules_dir / "mod.rs").open("w+") as fp:
|
||||
fp.write("\n\n")
|
||||
|
||||
# Create the snapshots subdirectory
|
||||
(plugin_dir / "snapshots").mkdir(exist_ok=True)
|
||||
|
||||
# Add the plugin to `rules/mod.rs`.
|
||||
with open(ROOT_DIR / "src/rules/mod.rs", "a") as fp:
|
||||
with (ROOT_DIR / "src/rules/mod.rs").open("a") as fp:
|
||||
fp.write(f"pub mod {dir_name(plugin)};")
|
||||
|
||||
# Add the relevant sections to `src/registry.rs`.
|
||||
content = (ROOT_DIR / "src/registry.rs").read_text()
|
||||
|
||||
with open(ROOT_DIR / "src/registry.rs", "w") as fp:
|
||||
with (ROOT_DIR / "src/registry.rs").open("w") as fp:
|
||||
for line in content.splitlines():
|
||||
if line.strip() == "// Ruff":
|
||||
indent = line.split("// Ruff")[0]
|
||||
indent = get_indent(line)
|
||||
|
||||
if line.strip() == "// ruff":
|
||||
fp.write(f"{indent}// {plugin}")
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "Ruff,":
|
||||
indent = line.split("Ruff,")[0]
|
||||
elif line.strip() == '/// Ruff-specific rules':
|
||||
fp.write(f"/// [{plugin}]({url})\n")
|
||||
fp.write(f'{indent}#[prefix = "{prefix_code}"]\n')
|
||||
fp.write(f"{indent}{pascal_case(plugin)},")
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == 'RuleOrigin::Ruff => "Ruff-specific rules",':
|
||||
indent = line.split('RuleOrigin::Ruff => "Ruff-specific rules",')[0]
|
||||
fp.write(f'{indent}RuleOrigin::{pascal_case(plugin)} => "{plugin}",')
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "RuleOrigin::Ruff => vec![RuleCodePrefix::RUF],":
|
||||
indent = line.split("RuleOrigin::Ruff => vec![RuleCodePrefix::RUF],")[0]
|
||||
fp.write(
|
||||
f"{indent}RuleOrigin::{pascal_case(plugin)} => vec![\n"
|
||||
f'{indent} todo!("Fill-in prefix after generating codes")\n'
|
||||
f"{indent}],"
|
||||
)
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "RuleOrigin::Ruff => None,":
|
||||
indent = line.split("RuleOrigin::Ruff => None,")[0]
|
||||
fp.write(f"{indent}RuleOrigin::{pascal_case(plugin)} => " f'Some(("{url}", &Platform::PyPI)),')
|
||||
fp.write("\n")
|
||||
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
# Add the relevant section to `src/violations.rs`.
|
||||
content = (ROOT_DIR / "src/violations.rs").read_text()
|
||||
|
||||
with open(ROOT_DIR / "src/violations.rs", "w") as fp:
|
||||
for line in content.splitlines():
|
||||
if line.strip() == "// Ruff":
|
||||
indent = line.split("// Ruff")[0]
|
||||
fp.write(f"{indent}// {plugin}")
|
||||
fp.write("\n")
|
||||
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
@@ -127,7 +99,7 @@ if __name__ == "__main__":
|
||||
description="Generate boilerplate for a new Flake8 plugin.",
|
||||
epilog=(
|
||||
"Example usage: python scripts/add_plugin.py flake8-pie "
|
||||
"--url https://pypi.org/project/flake8-pie/0.16.0/"
|
||||
"--url https://pypi.org/project/flake8-pie/"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
@@ -141,6 +113,13 @@ if __name__ == "__main__":
|
||||
type=str,
|
||||
help="The URL of the latest release in PyPI.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--prefix",
|
||||
required=False,
|
||||
default="TODO",
|
||||
type=str,
|
||||
help="Prefix code for the plugin. Leave empty to manually fill.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
main(plugin=args.plugin, url=args.url)
|
||||
main(plugin=args.plugin, url=args.url, prefix_code=args.prefix)
|
||||
|
||||
117
scripts/add_rule.py
Normal file → Executable file
117
scripts/add_rule.py
Normal file → Executable file
@@ -6,23 +6,12 @@ Example usage:
|
||||
python scripts/add_rule.py \
|
||||
--name PreferListBuiltin \
|
||||
--code PIE807 \
|
||||
--origin flake8-pie
|
||||
--linter flake8-pie
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
def dir_name(origin: str) -> str:
|
||||
return origin.replace("-", "_")
|
||||
|
||||
|
||||
def pascal_case(origin: str) -> str:
|
||||
"""Convert from snake-case to PascalCase."""
|
||||
return "".join(word.title() for word in origin.split("-"))
|
||||
from _utils import ROOT_DIR, dir_name, get_indent
|
||||
|
||||
|
||||
def snake_case(name: str) -> str:
|
||||
@@ -30,71 +19,87 @@ def snake_case(name: str) -> str:
|
||||
return "".join(f"_{word.lower()}" if word.isupper() else word for word in name).lstrip("_")
|
||||
|
||||
|
||||
def main(*, name: str, code: str, origin: str) -> None:
|
||||
def main(*, name: str, code: str, linter: str) -> None:
|
||||
# Create a test fixture.
|
||||
with open(
|
||||
ROOT_DIR / "resources/test/fixtures" / dir_name(origin) / f"{code}.py",
|
||||
"a",
|
||||
):
|
||||
with (ROOT_DIR / "resources/test/fixtures" / dir_name(linter) / f"{code}.py").open("a"):
|
||||
pass
|
||||
|
||||
plugin_module = ROOT_DIR / "src/rules" / dir_name(linter)
|
||||
rule_name_snake = snake_case(name)
|
||||
|
||||
# Add the relevant `#testcase` macro.
|
||||
mod_rs = ROOT_DIR / "src/rules" / dir_name(origin) / "mod.rs"
|
||||
mod_rs = plugin_module / "mod.rs"
|
||||
content = mod_rs.read_text()
|
||||
|
||||
with open(mod_rs, "w") as fp:
|
||||
with mod_rs.open("w") as fp:
|
||||
for line in content.splitlines():
|
||||
if line.strip() == "fn rules(rule_code: Rule, path: &Path) -> Result<()> {":
|
||||
indent = line.split("fn rules(rule_code: Rule, path: &Path) -> Result<()> {")[0]
|
||||
fp.write(f'{indent}#[test_case(Rule::{code}, Path::new("{code}.py"); "{code}")]')
|
||||
indent = get_indent(line)
|
||||
fp.write(f'{indent}#[test_case(Rule::{name}, Path::new("{code}.py"); "{code}")]')
|
||||
fp.write("\n")
|
||||
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
# Add the exports
|
||||
rules_dir = plugin_module / "rules"
|
||||
rules_mod = rules_dir / "mod.rs"
|
||||
|
||||
contents = rules_mod.read_text()
|
||||
parts = contents.split("\n\n")
|
||||
if len(parts) == 2:
|
||||
new_contents = parts[0] + "\n"
|
||||
new_contents += f"pub use {rule_name_snake}::{{{rule_name_snake}, {name}}};"
|
||||
new_contents += "\n"
|
||||
new_contents += "\n"
|
||||
new_contents += parts[1]
|
||||
new_contents += f"mod {rule_name_snake};"
|
||||
new_contents += "\n"
|
||||
rules_mod.write_text(new_contents)
|
||||
else:
|
||||
with rules_mod.open("a") as fp:
|
||||
fp.write(f"pub use {rule_name_snake}::{{{rule_name_snake}, {name}}};")
|
||||
fp.write("\n")
|
||||
fp.write(f"mod {rule_name_snake};")
|
||||
fp.write("\n")
|
||||
|
||||
# Add the relevant rule function.
|
||||
with open(ROOT_DIR / "src/rules" / dir_name(origin) / "rules.rs", "a") as fp:
|
||||
with (rules_dir / f"{rule_name_snake}.rs").open("w") as fp:
|
||||
fp.write(
|
||||
f"""
|
||||
/// {code}
|
||||
pub fn {snake_case(name)}(checker: &mut Checker) {{}}
|
||||
"""
|
||||
)
|
||||
fp.write("\n")
|
||||
"""use ruff_macros::derive_message_formats;
|
||||
|
||||
# Add the relevant struct to `src/violations.rs`.
|
||||
content = (ROOT_DIR / "src/violations.rs").read_text()
|
||||
use crate::define_violation;
|
||||
use crate::violation::Violation;
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
with open(ROOT_DIR / "src/violations.rs", "w") as fp:
|
||||
for line in content.splitlines():
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
if line.startswith(f"// {origin}"):
|
||||
fp.write(
|
||||
"""define_violation!(
|
||||
define_violation!(
|
||||
pub struct %s;
|
||||
);
|
||||
impl Violation for %s {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
todo!("Implement message")
|
||||
}
|
||||
|
||||
fn placeholder() -> Self {
|
||||
%s
|
||||
todo!("implement message");
|
||||
format!("TODO: write message")
|
||||
}
|
||||
}
|
||||
"""
|
||||
% (name, name, name)
|
||||
)
|
||||
fp.write("\n")
|
||||
% (name, name)
|
||||
)
|
||||
fp.write("\n")
|
||||
fp.write(
|
||||
f"""
|
||||
/// {code}
|
||||
pub fn {rule_name_snake}(checker: &mut Checker) {{}}
|
||||
"""
|
||||
)
|
||||
fp.write("\n")
|
||||
|
||||
# Add the relevant code-to-violation pair to `src/registry.rs`.
|
||||
content = (ROOT_DIR / "src/registry.rs").read_text()
|
||||
|
||||
seen_macro = False
|
||||
has_written = False
|
||||
with open(ROOT_DIR / "src/registry.rs", "w") as fp:
|
||||
with (ROOT_DIR / "src/registry.rs").open("w") as fp:
|
||||
for line in content.splitlines():
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
@@ -102,24 +107,26 @@ impl Violation for %s {
|
||||
if has_written:
|
||||
continue
|
||||
|
||||
if line.startswith("define_rule_mapping!"):
|
||||
if line.startswith("ruff_macros::define_rule_mapping!"):
|
||||
seen_macro = True
|
||||
continue
|
||||
|
||||
if not seen_macro:
|
||||
continue
|
||||
|
||||
if line.strip() == f"// {origin}":
|
||||
indent = line.split("//")[0]
|
||||
fp.write(f"{indent}{code} => violations::{name},")
|
||||
if line.strip() == f"// {linter}":
|
||||
indent = get_indent(line)
|
||||
fp.write(f"{indent}{code} => rules::{dir_name(linter)}::rules::{name},")
|
||||
fp.write("\n")
|
||||
has_written = True
|
||||
|
||||
assert has_written
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate boilerplate for a new rule.",
|
||||
epilog="python scripts/add_rule.py --name PreferListBuiltin --code PIE807 --origin flake8-pie",
|
||||
epilog="python scripts/add_rule.py --name PreferListBuiltin --code PIE807 --linter flake8-pie",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
@@ -134,11 +141,11 @@ if __name__ == "__main__":
|
||||
help="The code of the check to generate (e.g., 'A001').",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--origin",
|
||||
"--linter",
|
||||
type=str,
|
||||
required=True,
|
||||
help="The source with which the check originated (e.g., 'flake8-builtins').",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
main(name=args.name, code=args.code, origin=args.origin)
|
||||
main(name=args.name, code=args.code, linter=args.linter)
|
||||
|
||||
9
scripts/pyproject.toml
Normal file
9
scripts/pyproject.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[tool.ruff]
|
||||
select = ["ALL"]
|
||||
ignore = [
|
||||
"S101", # assert-used
|
||||
"PLR2004", # magic-value-comparison
|
||||
]
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
convention = "pep257"
|
||||
1
setup.py
1
setup.py
@@ -13,6 +13,7 @@ Please use `python -m pip install .` instead.
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
"abc".isidentifier()
|
||||
|
||||
# The below code will never execute, however GitHub is particularly
|
||||
# picky about where it finds Python packaging metadata.
|
||||
|
||||
@@ -295,7 +295,7 @@ pub enum ComparableExpr<'a> {
|
||||
orelse: Box<ComparableExpr<'a>>,
|
||||
},
|
||||
Dict {
|
||||
keys: Vec<ComparableExpr<'a>>,
|
||||
keys: Vec<Option<ComparableExpr<'a>>>,
|
||||
values: Vec<ComparableExpr<'a>>,
|
||||
},
|
||||
Set {
|
||||
@@ -424,7 +424,10 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
orelse: orelse.into(),
|
||||
},
|
||||
ExprKind::Dict { keys, values } => Self::Dict {
|
||||
keys: keys.iter().map(std::convert::Into::into).collect(),
|
||||
keys: keys
|
||||
.iter()
|
||||
.map(|expr| expr.as_ref().map(std::convert::Into::into))
|
||||
.collect(),
|
||||
values: values.iter().map(std::convert::Into::into).collect(),
|
||||
},
|
||||
ExprKind::Set { elts } => Self::Set {
|
||||
|
||||
40
src/ast/hashable.rs
Normal file
40
src/ast/hashable.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use std::hash::Hash;
|
||||
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::comparable::ComparableExpr;
|
||||
|
||||
/// Wrapper around `Expr` that implements `Hash` and `PartialEq`.
|
||||
pub struct HashableExpr<'a>(&'a Expr);
|
||||
|
||||
impl Hash for HashableExpr<'_> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
let comparable = ComparableExpr::from(self.0);
|
||||
comparable.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Self> for HashableExpr<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let comparable = ComparableExpr::from(self.0);
|
||||
comparable == ComparableExpr::from(other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for HashableExpr<'_> {}
|
||||
|
||||
impl<'a> From<&'a Expr> for HashableExpr<'a> {
|
||||
fn from(expr: &'a Expr) -> Self {
|
||||
Self(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HashableExpr<'a> {
|
||||
pub(crate) fn from_expr(expr: &'a Expr) -> Self {
|
||||
Self(expr)
|
||||
}
|
||||
|
||||
pub(crate) fn as_expr(&self) -> &'a Expr {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ where
|
||||
}
|
||||
ExprKind::Dict { keys, values } => values
|
||||
.iter()
|
||||
.chain(keys.iter())
|
||||
.chain(keys.iter().flatten())
|
||||
.any(|expr| any_over_expr(expr, func)),
|
||||
ExprKind::Set { elts } | ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
elts.iter().any(|expr| any_over_expr(expr, func))
|
||||
@@ -366,7 +366,7 @@ pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
|
||||
|
||||
/// Returns `true` if a statement or expression includes at least one comment.
|
||||
pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
|
||||
lexer::make_tokenizer(&locator.slice_source_code_range(&range))
|
||||
lexer::make_tokenizer(locator.slice_source_code_range(&range))
|
||||
.any(|result| result.map_or(false, |(_, tok, _)| matches!(tok, Tok::Comment(..))))
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
) {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(&contents, stmt.location).flatten() {
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt.location).flatten() {
|
||||
if matches!(tok, Tok::Name { .. }) {
|
||||
return Range::new(start, end);
|
||||
}
|
||||
@@ -515,7 +515,7 @@ pub fn binding_range(binding: &Binding, locator: &Locator) -> Range {
|
||||
// Return the ranges of `Name` tokens within a specified node.
|
||||
pub fn find_names<T>(located: &Located<T>, locator: &Locator) -> Vec<Range> {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(located));
|
||||
lexer::make_tokenizer_located(&contents, located.location)
|
||||
lexer::make_tokenizer_located(contents, located.location)
|
||||
.flatten()
|
||||
.filter(|(_, tok, _)| matches!(tok, Tok::Name { .. }))
|
||||
.map(|(start, _, end)| Range {
|
||||
@@ -535,7 +535,7 @@ pub fn excepthandler_name_range(handler: &Excepthandler, locator: &Locator) -> O
|
||||
let type_end_location = type_.end_location.unwrap();
|
||||
let contents =
|
||||
locator.slice_source_code_range(&Range::new(type_end_location, body[0].location));
|
||||
let range = lexer::make_tokenizer_located(&contents, type_end_location)
|
||||
let range = lexer::make_tokenizer_located(contents, type_end_location)
|
||||
.flatten()
|
||||
.tuple_windows()
|
||||
.find(|(tok, next_tok)| {
|
||||
@@ -562,7 +562,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||
location: handler.location,
|
||||
end_location: end,
|
||||
});
|
||||
let range = lexer::make_tokenizer_located(&contents, handler.location)
|
||||
let range = lexer::make_tokenizer_located(contents, handler.location)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Except { .. }))
|
||||
.map(|(location, _, end_location)| Range {
|
||||
@@ -576,7 +576,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||
/// Find f-strings that don't contain any formatted values in a `JoinedStr`.
|
||||
pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
lexer::make_tokenizer_located(&contents, expr.location)
|
||||
lexer::make_tokenizer_located(contents, expr.location)
|
||||
.flatten()
|
||||
.filter_map(|(location, tok, end_location)| match tok {
|
||||
Tok::String {
|
||||
@@ -630,7 +630,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
.expect("Expected orelse to be non-empty")
|
||||
.location,
|
||||
});
|
||||
let range = lexer::make_tokenizer_located(&contents, body_end)
|
||||
let range = lexer::make_tokenizer_located(contents, body_end)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Else))
|
||||
.map(|(location, _, end_location)| Range {
|
||||
@@ -646,7 +646,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
/// Return the `Range` of the first `Tok::Colon` token in a `Range`.
|
||||
pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
|
||||
let contents = locator.slice_source_code_range(&range);
|
||||
let range = lexer::make_tokenizer_located(&contents, range.location)
|
||||
let range = lexer::make_tokenizer_located(contents, range.location)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Colon))
|
||||
.map(|(location, _, end_location)| Range {
|
||||
@@ -676,7 +676,7 @@ pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
_ => return None,
|
||||
};
|
||||
let contents = locator.slice_source_code_range(&Range::new(start, end));
|
||||
let range = lexer::make_tokenizer_located(&contents, start)
|
||||
let range = lexer::make_tokenizer_located(contents, start)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Elif | Tok::Else))
|
||||
.map(|(location, _, end_location)| Range {
|
||||
|
||||
@@ -2,6 +2,7 @@ pub mod branch_detection;
|
||||
pub mod cast;
|
||||
pub mod comparable;
|
||||
pub mod function_type;
|
||||
pub mod hashable;
|
||||
pub mod helpers;
|
||||
pub mod operations;
|
||||
pub mod relocate;
|
||||
|
||||
@@ -39,7 +39,7 @@ pub fn relocate_expr(expr: &mut Expr, location: Range) {
|
||||
relocate_expr(orelse, location);
|
||||
}
|
||||
ExprKind::Dict { keys, values } => {
|
||||
for expr in keys {
|
||||
for expr in keys.iter_mut().flatten() {
|
||||
relocate_expr(expr, location);
|
||||
}
|
||||
for expr in values {
|
||||
|
||||
@@ -288,7 +288,7 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
|
||||
visitor.visit_expr(orelse);
|
||||
}
|
||||
ExprKind::Dict { keys, values } => {
|
||||
for expr in keys {
|
||||
for expr in keys.iter().flatten() {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
for expr in values {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::borrow::Cow;
|
||||
use std::str::Lines;
|
||||
|
||||
use rustpython_ast::{Located, Location};
|
||||
@@ -7,7 +6,7 @@ use crate::ast::types::Range;
|
||||
use crate::source_code::Locator;
|
||||
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<Cow<'a, str>> {
|
||||
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<&'a str> {
|
||||
let range = Range::from_located(located);
|
||||
let indentation = locator.slice_source_code_range(&Range::new(
|
||||
Location::new(range.location.row(), 0),
|
||||
|
||||
@@ -80,7 +80,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
/// of a multi-statement line.
|
||||
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
let contents = locator.slice_source_code_at(stmt.end_location.unwrap());
|
||||
for (row, line) in LinesWithTrailingNewline::from(&contents).enumerate() {
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with(';') {
|
||||
let column = line
|
||||
@@ -103,7 +103,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
|
||||
let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
|
||||
let contents = locator.slice_source_code_at(start_location);
|
||||
for (row, line) in LinesWithTrailingNewline::from(&contents).enumerate() {
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
// Skip past any continuations.
|
||||
if trimmed.starts_with('\\') {
|
||||
@@ -202,7 +202,7 @@ pub fn remove_unused_imports<'a>(
|
||||
indexer: &Indexer,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut tree = match_module(&module_text)?;
|
||||
let mut tree = match_module(module_text)?;
|
||||
|
||||
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
|
||||
bail!("Expected Statement::Simple");
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ropey::RopeBuilder;
|
||||
use rustpython_ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -14,10 +12,7 @@ pub mod fixer;
|
||||
pub mod helpers;
|
||||
|
||||
/// Auto-fix errors in a file, and write the fixed source code to disk.
|
||||
pub fn fix_file<'a>(
|
||||
diagnostics: &'a [Diagnostic],
|
||||
locator: &'a Locator<'a>,
|
||||
) -> Option<(Cow<'a, str>, usize)> {
|
||||
pub fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option<(String, usize)> {
|
||||
if diagnostics.iter().all(|check| check.fix.is_none()) {
|
||||
return None;
|
||||
}
|
||||
@@ -32,8 +27,8 @@ pub fn fix_file<'a>(
|
||||
fn apply_fixes<'a>(
|
||||
fixes: impl Iterator<Item = &'a Fix>,
|
||||
locator: &'a Locator<'a>,
|
||||
) -> (Cow<'a, str>, usize) {
|
||||
let mut output = RopeBuilder::new();
|
||||
) -> (String, usize) {
|
||||
let mut output = String::new();
|
||||
let mut last_pos: Location = Location::new(1, 0);
|
||||
let mut applied: BTreeSet<&Fix> = BTreeSet::default();
|
||||
let mut num_fixed: usize = 0;
|
||||
@@ -54,10 +49,10 @@ fn apply_fixes<'a>(
|
||||
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice_source_code_range(&Range::new(last_pos, fix.location));
|
||||
output.append(&slice);
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the patch itself.
|
||||
output.append(&fix.content);
|
||||
output.push_str(&fix.content);
|
||||
|
||||
// Track that the fix was applied.
|
||||
last_pos = fix.end_location;
|
||||
@@ -67,9 +62,9 @@ fn apply_fixes<'a>(
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = locator.slice_source_code_at(last_pos);
|
||||
output.append(&slice);
|
||||
output.push_str(slice);
|
||||
|
||||
(Cow::from(output.finish()), num_fixed)
|
||||
(output, num_fixed)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -35,9 +35,9 @@ use crate::rules::{
|
||||
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
|
||||
flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger,
|
||||
flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_pie, flake8_print,
|
||||
flake8_pytest_style, flake8_return, flake8_simplify, flake8_tidy_imports,
|
||||
flake8_unused_arguments, mccabe, pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes,
|
||||
pygrep_hooks, pylint, pyupgrade, ruff,
|
||||
flake8_pytest_style, flake8_return, flake8_simplify, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, flake8_use_pathlib, mccabe, pandas_vet, pep8_naming, pycodestyle,
|
||||
pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
};
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -273,7 +273,7 @@ impl<'a> Checker<'a> {
|
||||
Location::new(*noqa_lineno, 0),
|
||||
Location::new(noqa_lineno + 1, 0),
|
||||
));
|
||||
match noqa::extract_noqa_directive(&line) {
|
||||
match noqa::extract_noqa_directive(line) {
|
||||
Directive::None => false,
|
||||
Directive::All(..) => true,
|
||||
Directive::Codes(.., codes) => noqa::includes(code, &codes),
|
||||
@@ -1230,6 +1230,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::ImportAliasIsNotConventional)
|
||||
{
|
||||
let full_name = helpers::format_import_from_member(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
&alias.node.name,
|
||||
);
|
||||
if let Some(diagnostic) =
|
||||
flake8_import_conventions::rules::check_conventional_import(
|
||||
stmt,
|
||||
&full_name,
|
||||
alias.node.asname.as_deref(),
|
||||
&self.settings.flake8_import_conventions.aliases,
|
||||
)
|
||||
{
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
if self
|
||||
.settings
|
||||
@@ -1355,8 +1377,18 @@ where
|
||||
if self.settings.rules.enabled(&Rule::IfTuple) {
|
||||
pyflakes::rules::if_tuple(self, stmt, test);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::EmptyTypeCheckingBlock) {
|
||||
flake8_type_checking::rules::empty_type_checking_block(self, test, body);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NestedIfStatements) {
|
||||
flake8_simplify::rules::nested_if_statements(self, stmt);
|
||||
flake8_simplify::rules::nested_if_statements(
|
||||
self,
|
||||
stmt,
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
self.current_stmt_parent().map(Into::into),
|
||||
);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
@@ -1382,6 +1414,15 @@ where
|
||||
self.current_stmt_parent().map(std::convert::Into::into),
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::PreferTypeError) {
|
||||
tryceratops::rules::prefer_type_error(
|
||||
self,
|
||||
body,
|
||||
test,
|
||||
orelse,
|
||||
self.current_stmt_parent().map(Into::into),
|
||||
);
|
||||
}
|
||||
}
|
||||
StmtKind::Assert { test, msg } => {
|
||||
if self.settings.rules.enabled(&Rule::AssertTuple) {
|
||||
@@ -1535,6 +1576,15 @@ where
|
||||
self, body, handlers, finalbody,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::TryConsiderElse) {
|
||||
tryceratops::rules::try_consider_else(self, body, orelse);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::VerboseRaise) {
|
||||
tryceratops::rules::verbose_raise(self, handlers);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RaiseWithinTry) {
|
||||
tryceratops::rules::raise_within_try(self, body);
|
||||
}
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
if self.settings.rules.enabled(&Rule::DoNotAssignLambda) {
|
||||
@@ -2160,6 +2210,11 @@ where
|
||||
flake8_bugbear::rules::zip_without_explicit_strict(self, expr, func, keywords);
|
||||
}
|
||||
|
||||
// flake8-pie
|
||||
if self.settings.rules.enabled(&Rule::NoUnnecessaryDictKwargs) {
|
||||
flake8_pie::rules::no_unnecessary_dict_kwargs(self, expr, keywords);
|
||||
}
|
||||
|
||||
// flake8-bandit
|
||||
if self.settings.rules.enabled(&Rule::ExecUsed) {
|
||||
if let Some(diagnostic) = flake8_bandit::rules::exec_used(expr, func) {
|
||||
@@ -2206,6 +2261,15 @@ where
|
||||
if self.settings.rules.enabled(&Rule::RequestWithoutTimeout) {
|
||||
flake8_bandit::rules::request_without_timeout(self, func, args, keywords);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::LoggingConfigInsecureListen)
|
||||
{
|
||||
flake8_bandit::rules::logging_config_insecure_listen(
|
||||
self, func, args, keywords,
|
||||
);
|
||||
}
|
||||
|
||||
// flake8-comprehensions
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryGeneratorList) {
|
||||
@@ -2502,6 +2566,35 @@ where
|
||||
{
|
||||
flake8_simplify::rules::open_file_with_context_handler(self, func);
|
||||
}
|
||||
|
||||
// flake8-use-pathlib
|
||||
if self.settings.rules.enabled(&Rule::PathlibAbspath)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibChmod)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibMkdir)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibMakedirs)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibRename)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibReplace)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibRmdir)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibRemove)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibUnlink)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibGetcwd)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibExists)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibExpanduser)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibIsDir)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibIsFile)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibIsLink)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibReadlink)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibStat)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibIsAbs)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibJoin)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibBasename)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibSamefile)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibSplitext)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibOpen)
|
||||
|| self.settings.rules.enabled(&Rule::PathlibPyPath)
|
||||
{
|
||||
flake8_use_pathlib::helpers::replaceable_by_pathlib(self, func);
|
||||
}
|
||||
}
|
||||
ExprKind::Dict { keys, values } => {
|
||||
if self
|
||||
@@ -2515,6 +2608,10 @@ where
|
||||
{
|
||||
pyflakes::rules::repeated_keys(self, keys, values);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::NoUnnecessarySpread) {
|
||||
flake8_pie::rules::no_unnecessary_spread(self, keys, values);
|
||||
}
|
||||
}
|
||||
ExprKind::Yield { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
@@ -2618,7 +2715,7 @@ where
|
||||
.enabled(&Rule::PercentFormatUnsupportedFormatCharacter)
|
||||
{
|
||||
let location = Range::from_located(expr);
|
||||
match pyflakes::cformat::CFormatSummary::try_from(value.as_ref()) {
|
||||
match pyflakes::cformat::CFormatSummary::try_from(value.as_str()) {
|
||||
Err(CFormatError {
|
||||
typ: CFormatErrorType::UnsupportedFormatChar(c),
|
||||
..
|
||||
@@ -2713,6 +2810,10 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::PrintfStringFormatting) {
|
||||
pyupgrade::rules::printf_string_formatting(self, expr, left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
@@ -3108,7 +3209,7 @@ where
|
||||
// Ex) TypedDict("a", {"a": int})
|
||||
if args.len() > 1 {
|
||||
if let ExprKind::Dict { keys, values } = &args[1].node {
|
||||
for key in keys {
|
||||
for key in keys.iter().flatten() {
|
||||
self.in_type_definition = false;
|
||||
self.visit_expr(key);
|
||||
self.in_type_definition = prev_in_type_definition;
|
||||
@@ -3259,6 +3360,9 @@ where
|
||||
body,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ReraiseNoCause) {
|
||||
tryceratops::rules::reraise_no_cause(self, body);
|
||||
}
|
||||
match name {
|
||||
Some(name) => {
|
||||
if self.settings.rules.enabled(&Rule::AmbiguousVariableName) {
|
||||
@@ -3571,7 +3675,6 @@ impl<'a> Checker<'a> {
|
||||
{
|
||||
let binding_index = self.bindings.len();
|
||||
|
||||
let mut overridden = None;
|
||||
if let Some((stack_index, scope_index)) = self
|
||||
.scope_stack
|
||||
.iter()
|
||||
@@ -3603,7 +3706,6 @@ impl<'a> Checker<'a> {
|
||||
| BindingKind::FutureImportation
|
||||
);
|
||||
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
|
||||
overridden = Some((*scope_index, *existing_binding_index));
|
||||
if self.settings.rules.enabled(&Rule::ImportShadowedByLoopVar) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::ImportShadowedByLoopVar(
|
||||
@@ -3623,7 +3725,6 @@ impl<'a> Checker<'a> {
|
||||
cast::decorator_list(existing.source.as_ref().unwrap()),
|
||||
))
|
||||
{
|
||||
overridden = Some((*scope_index, *existing_binding_index));
|
||||
if self.settings.rules.enabled(&Rule::RedefinedWhileUnused) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
violations::RedefinedWhileUnused(
|
||||
@@ -3643,13 +3744,6 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// If we're about to lose the binding, store it as overridden.
|
||||
if let Some((scope_index, binding_index)) = overridden {
|
||||
self.scopes[scope_index]
|
||||
.overridden
|
||||
.push((name, binding_index));
|
||||
}
|
||||
|
||||
// Assume the rebound name is used as a global or within a loop.
|
||||
let scope = self.current_scope();
|
||||
let binding = match scope.values.get(&name) {
|
||||
@@ -4497,6 +4591,7 @@ impl<'a> Checker<'a> {
|
||||
.rules
|
||||
.enabled(&Rule::UsesRPrefixForBackslashedContent)
|
||||
|| self.settings.rules.enabled(&Rule::EndsInPeriod)
|
||||
|| self.settings.rules.enabled(&Rule::NonImperativeMood)
|
||||
|| self.settings.rules.enabled(&Rule::NoSignature)
|
||||
|| self.settings.rules.enabled(&Rule::FirstLineCapitalized)
|
||||
|| self.settings.rules.enabled(&Rule::NoThisPrefix)
|
||||
@@ -4572,12 +4667,13 @@ impl<'a> Checker<'a> {
|
||||
Location::new(expr.location.row(), 0),
|
||||
Location::new(expr.location.row(), expr.location.column()),
|
||||
));
|
||||
let body = pydocstyle::helpers::raw_contents(&contents);
|
||||
|
||||
let body = pydocstyle::helpers::raw_contents(contents);
|
||||
let docstring = Docstring {
|
||||
kind: definition.kind,
|
||||
expr,
|
||||
contents: &contents,
|
||||
indentation: &indentation,
|
||||
contents,
|
||||
indentation,
|
||||
body,
|
||||
};
|
||||
|
||||
@@ -4645,6 +4741,9 @@ impl<'a> Checker<'a> {
|
||||
if self.settings.rules.enabled(&Rule::EndsInPeriod) {
|
||||
pydocstyle::rules::ends_with_period(self, &docstring);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NonImperativeMood) {
|
||||
pydocstyle::rules::non_imperative_mood::non_imperative_mood(self, &docstring);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NoSignature) {
|
||||
pydocstyle::rules::no_signature(self, &docstring);
|
||||
}
|
||||
@@ -4718,6 +4817,7 @@ impl<'a> Checker<'a> {
|
||||
name,
|
||||
located,
|
||||
flake8_builtins::types::ShadowingType::Attribute,
|
||||
&self.settings.flake8_builtins.builtins_ignorelist,
|
||||
) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -4728,6 +4828,7 @@ impl<'a> Checker<'a> {
|
||||
name,
|
||||
located,
|
||||
flake8_builtins::types::ShadowingType::Variable,
|
||||
&self.settings.flake8_builtins.builtins_ignorelist,
|
||||
) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -4741,6 +4842,7 @@ impl<'a> Checker<'a> {
|
||||
name,
|
||||
arg,
|
||||
flake8_builtins::types::ShadowingType::Argument,
|
||||
&self.settings.flake8_builtins.builtins_ignorelist,
|
||||
) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,16 @@ use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_file_path(path: &Path, settings: &Settings) -> Vec<Diagnostic> {
|
||||
pub fn check_file_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
settings: &Settings,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(&Rule::ImplicitNamespacePackage) {
|
||||
if let Some(diagnostic) = implicit_namespace_package(path) {
|
||||
if let Some(diagnostic) = implicit_namespace_package(path, package) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
//! Lint rules based on checking raw physical lines.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective};
|
||||
use crate::rules::flake8_executable::rules::{
|
||||
shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace,
|
||||
};
|
||||
use crate::rules::pycodestyle::rules::{
|
||||
doc_line_too_long, line_too_long, no_newline_at_end_of_file,
|
||||
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
|
||||
};
|
||||
use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
|
||||
use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
|
||||
use crate::settings::{flags, Settings};
|
||||
|
||||
pub fn check_lines(
|
||||
path: &Path,
|
||||
contents: &str,
|
||||
commented_lines: &[usize],
|
||||
doc_lines: &[usize],
|
||||
@@ -16,8 +23,14 @@ pub fn check_lines(
|
||||
autofix: flags::Autofix,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
let mut has_any_shebang = false;
|
||||
|
||||
let enforce_blanket_noqa = settings.rules.enabled(&Rule::BlanketNOQA);
|
||||
let enforce_shebang_not_executable = settings.rules.enabled(&Rule::ShebangNotExecutable);
|
||||
let enforce_shebang_missing = settings.rules.enabled(&Rule::ShebangMissingExecutableFile);
|
||||
let enforce_shebang_whitespace = settings.rules.enabled(&Rule::ShebangWhitespace);
|
||||
let enforce_shebang_newline = settings.rules.enabled(&Rule::ShebangNewline);
|
||||
let enforce_shebang_python = settings.rules.enabled(&Rule::ShebangPython);
|
||||
let enforce_blanket_type_ignore = settings.rules.enabled(&Rule::BlanketTypeIgnore);
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(&Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = settings.rules.enabled(&Rule::LineTooLong);
|
||||
@@ -25,6 +38,14 @@ pub fn check_lines(
|
||||
let enforce_unnecessary_coding_comment = settings
|
||||
.rules
|
||||
.enabled(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
let enforce_mixed_spaces_and_tabs = settings.rules.enabled(&Rule::MixedSpacesAndTabs);
|
||||
|
||||
let fix_unnecessary_coding_comment = matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings
|
||||
.rules
|
||||
.should_fix(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
let fix_shebang_whitespace = matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::ShebangWhitespace);
|
||||
|
||||
let mut commented_lines_iter = commented_lines.iter().peekable();
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
@@ -35,14 +56,9 @@ pub fn check_lines(
|
||||
{
|
||||
if enforce_unnecessary_coding_comment {
|
||||
if index < 2 {
|
||||
if let Some(diagnostic) = unnecessary_coding_comment(
|
||||
index,
|
||||
line,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings
|
||||
.rules
|
||||
.should_fix(&Rule::PEP3120UnnecessaryCodingComment),
|
||||
) {
|
||||
if let Some(diagnostic) =
|
||||
unnecessary_coding_comment(index, line, fix_unnecessary_coding_comment)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -59,6 +75,42 @@ pub fn check_lines(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_shebang_missing
|
||||
|| enforce_shebang_not_executable
|
||||
|| enforce_shebang_whitespace
|
||||
|| enforce_shebang_newline
|
||||
|| enforce_shebang_python
|
||||
{
|
||||
let shebang = extract_shebang(line);
|
||||
if enforce_shebang_not_executable {
|
||||
if let Some(diagnostic) = shebang_not_executable(path, index, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_missing {
|
||||
if !has_any_shebang && matches!(shebang, ShebangDirective::Match(_, _, _, _)) {
|
||||
has_any_shebang = true;
|
||||
}
|
||||
}
|
||||
if enforce_shebang_whitespace {
|
||||
if let Some(diagnostic) =
|
||||
shebang_whitespace(index, &shebang, fix_shebang_whitespace)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_newline {
|
||||
if let Some(diagnostic) = shebang_newline(index, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_python {
|
||||
if let Some(diagnostic) = shebang_python(index, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while doc_lines_iter
|
||||
@@ -72,6 +124,12 @@ pub fn check_lines(
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_mixed_spaces_and_tabs {
|
||||
if let Some(diagnostic) = mixed_spaces_and_tabs(index, line) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_line_too_long {
|
||||
if let Some(diagnostic) = line_too_long(index, line, settings) {
|
||||
diagnostics.push(diagnostic);
|
||||
@@ -89,12 +147,20 @@ pub fn check_lines(
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_shebang_missing && !has_any_shebang {
|
||||
if let Some(diagnostic) = shebang_missing(path) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use super::check_lines;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -104,6 +170,7 @@ mod tests {
|
||||
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
|
||||
let check_with_max_line_length = |line_length: usize| {
|
||||
check_lines(
|
||||
Path::new("foo.py"),
|
||||
line,
|
||||
&[],
|
||||
&[],
|
||||
|
||||
@@ -6,7 +6,8 @@ use rustpython_parser::ast::Location;
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, Rule, CODE_REDIRECTS};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, Rule};
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::violations::UnusedCodes;
|
||||
use crate::{noqa, violations};
|
||||
@@ -123,7 +124,7 @@ pub fn check_noqa(
|
||||
let mut valid_codes = vec![];
|
||||
let mut self_ignore = false;
|
||||
for code in codes {
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, |r| r.code());
|
||||
let code = get_redirect_target(code).unwrap_or(code);
|
||||
if code == Rule::UnusedNOQA.code() {
|
||||
self_ignore = true;
|
||||
break;
|
||||
|
||||
@@ -6,7 +6,8 @@ use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, ruff,
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, pyupgrade,
|
||||
ruff,
|
||||
};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
@@ -45,6 +46,7 @@ pub fn check_tokens(
|
||||
.rules
|
||||
.enabled(&Rule::TrailingCommaOnBareTupleProhibited)
|
||||
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
|
||||
let enforce_extraneous_parenthesis = settings.rules.enabled(&Rule::ExtraneousParentheses);
|
||||
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
@@ -122,9 +124,12 @@ pub fn check_tokens(
|
||||
// ISC001, ISC002
|
||||
if enforce_implicit_string_concatenation {
|
||||
diagnostics.extend(
|
||||
flake8_implicit_str_concat::rules::implicit(tokens)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
flake8_implicit_str_concat::rules::implicit(
|
||||
tokens,
|
||||
&settings.flake8_implicit_str_concat,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,5 +142,13 @@ pub fn check_tokens(
|
||||
);
|
||||
}
|
||||
|
||||
// UP034
|
||||
if enforce_extraneous_parenthesis {
|
||||
diagnostics.extend(
|
||||
pyupgrade::rules::extraneous_parentheses(tokens, locator, settings, autofix)
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user