Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dca994d05f | ||
|
|
9944246f98 | ||
|
|
82b0b7941a | ||
|
|
72453695d6 | ||
|
|
1617d715f2 | ||
|
|
c4a7344791 | ||
|
|
ea9acda732 | ||
|
|
6c8021e970 | ||
|
|
61b6ad46ea | ||
|
|
041d8108e6 | ||
|
|
e2c4a098de | ||
|
|
e865f58426 | ||
|
|
23b4e16b1d | ||
|
|
ae2ac905dc | ||
|
|
55619b321a | ||
|
|
6f31b002f8 | ||
|
|
1a79965aa0 | ||
|
|
16da183f8e | ||
|
|
3f689917cb | ||
|
|
a4a215e8a3 | ||
|
|
aa1c884910 | ||
|
|
7fb55c6d99 | ||
|
|
04ea523ad8 | ||
|
|
9897f81cf3 | ||
|
|
1a2559b001 | ||
|
|
721a1e9443 | ||
|
|
f38bba18ee | ||
|
|
14cf36f922 | ||
|
|
d28e026525 | ||
|
|
d19a8aa54d | ||
|
|
f299940452 | ||
|
|
e1ab7163ac | ||
|
|
9edc479c6c | ||
|
|
560558b814 | ||
|
|
bef601b994 | ||
|
|
7445d00b88 | ||
|
|
7c78d4e103 | ||
|
|
8b14f1b8cc | ||
|
|
5a6b51e623 | ||
|
|
0b60242fb7 | ||
|
|
65b77feeb8 | ||
|
|
04b9c0a31d | ||
|
|
49dc8231be | ||
|
|
92ca114882 | ||
|
|
553bc7443a | ||
|
|
a3af6c1ea5 | ||
|
|
b50016fe89 | ||
|
|
2cf2805848 | ||
|
|
33fbef7700 | ||
|
|
68668a584b | ||
|
|
6cd8655d29 | ||
|
|
72a9bd3cfb | ||
|
|
58aac21a36 | ||
|
|
77e0be3464 | ||
|
|
bd08fc359d | ||
|
|
19ad6ab4f5 | ||
|
|
4b2df99e78 | ||
|
|
66975876b2 | ||
|
|
7316b120ba | ||
|
|
fec887e481 | ||
|
|
bdd32c0850 | ||
|
|
10dcd5fd0a | ||
|
|
b922e6ecc8 | ||
|
|
f59799e0c4 | ||
|
|
814ddeb7ea | ||
|
|
9315b9f459 | ||
|
|
113b5a10bf | ||
|
|
fe77fb70a1 | ||
|
|
c3f6170503 | ||
|
|
a46160f0e2 | ||
|
|
9a66cf2ffb | ||
|
|
3205473612 | ||
|
|
0bb8b14ae1 | ||
|
|
9cf4621071 | ||
|
|
bbc9ed1b21 | ||
|
|
517ca2604a | ||
|
|
348ff509c0 | ||
|
|
fb545551f8 | ||
|
|
3b33a431d6 | ||
|
|
bc95690725 | ||
|
|
9dc788d089 | ||
|
|
15f63494a7 | ||
|
|
68668c20e8 | ||
|
|
9462335371 | ||
|
|
3dd6522b4f | ||
|
|
6b935121a7 | ||
|
|
8d9d9b3204 | ||
|
|
0a506eff34 | ||
|
|
7c7489c1dd | ||
|
|
ae90eccb7f | ||
|
|
7a61edbe46 | ||
|
|
59615486d8 | ||
|
|
ce116a80ad | ||
|
|
731fba9006 | ||
|
|
9bcf194fdc | ||
|
|
58949b564e | ||
|
|
6b9e57fb78 | ||
|
|
cb119401a7 | ||
|
|
0b9188011b | ||
|
|
b657d912d9 | ||
|
|
1559671093 | ||
|
|
70a53bf12b | ||
|
|
cd1e07f37c | ||
|
|
7bd6db62d9 | ||
|
|
f8b49f308d | ||
|
|
b1f9c7b6bd | ||
|
|
0867d2ded9 | ||
|
|
3f597a3b30 | ||
|
|
6733aad216 | ||
|
|
89980ad651 | ||
|
|
38f896502a | ||
|
|
7cab541343 | ||
|
|
1a3d2ead41 | ||
|
|
f96c64b40d | ||
|
|
0791869451 | ||
|
|
965918744b | ||
|
|
6b4aedb366 | ||
|
|
8123e3e94e | ||
|
|
9f9a545c51 | ||
|
|
5bf8219db3 | ||
|
|
529513bf02 | ||
|
|
124782771f | ||
|
|
98cab5cdba | ||
|
|
40f38c94a5 | ||
|
|
7839204bf7 | ||
|
|
e63ea704f0 | ||
|
|
4be09b45ea | ||
|
|
13e8ed0a0a | ||
|
|
4161d4ae32 | ||
|
|
99f7854d8c | ||
|
|
a580d1a858 | ||
|
|
86806a9e39 |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -82,7 +82,7 @@ jobs:
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
|
||||
|
||||
cargo_test:
|
||||
name: "cargo test"
|
||||
|
||||
8
.github/workflows/flake8-to-ruff.yaml
vendored
8
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -34,7 +34,6 @@ jobs:
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel - x86_64
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
@@ -61,7 +60,6 @@ jobs:
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel - universal2
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*universal2.whl --force-reinstall
|
||||
@@ -93,7 +91,6 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -121,7 +118,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
@@ -148,7 +144,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --no-default-features --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- uses: uraimo/run-on-arch-action@v2.0.5
|
||||
if: matrix.target != 'ppc64'
|
||||
name: Install built wheel
|
||||
@@ -187,7 +182,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
uses: addnab/docker-run-action@v3
|
||||
@@ -223,7 +217,6 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
maturin-version: "v0.13.0"
|
||||
- uses: uraimo/run-on-arch-action@master
|
||||
name: Install built wheel
|
||||
with:
|
||||
@@ -261,7 +254,6 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
maturin-version: "v0.13.0"
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -i pypy${{ matrix.python-version }} -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
|
||||
8
.github/workflows/ruff.yaml
vendored
8
.github/workflows/ruff.yaml
vendored
@@ -36,7 +36,6 @@ jobs:
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel - x86_64
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
@@ -63,7 +62,6 @@ jobs:
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel - universal2
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
|
||||
@@ -95,7 +93,6 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -123,7 +120,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
@@ -150,7 +146,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --no-default-features --release --out dist
|
||||
maturin-version: "v0.13.0"
|
||||
- uses: uraimo/run-on-arch-action@v2.0.5
|
||||
if: matrix.target != 'ppc64'
|
||||
name: Install built wheel
|
||||
@@ -189,7 +184,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
maturin-version: "v0.13.0"
|
||||
- name: Install built wheel
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
uses: addnab/docker-run-action@v3
|
||||
@@ -225,7 +219,6 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
maturin-version: "v0.13.0"
|
||||
- uses: uraimo/run-on-arch-action@master
|
||||
name: Install built wheel
|
||||
with:
|
||||
@@ -263,7 +256,6 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
maturin-version: "v0.13.0"
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -i pypy${{ matrix.python-version }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.128
|
||||
rev: v0.0.145
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
698
Cargo.lock
generated
698
Cargo.lock
generated
@@ -94,133 +94,6 @@ dependencies = [
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"autocfg",
|
||||
"concurrent-queue",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"log",
|
||||
"parking",
|
||||
"polling",
|
||||
"slab",
|
||||
"socket2",
|
||||
"waker-fn",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"autocfg",
|
||||
"blocking",
|
||||
"cfg-if 1.0.0",
|
||||
"event-listener",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"signal-hook",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-process",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"gloo-timers",
|
||||
"kv-log-macro",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -238,15 +111,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
@@ -283,50 +147,6 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array 0.14.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-task",
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
@@ -357,46 +177,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
name = "cachedir"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cacache"
|
||||
version = "10.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c13caedf5b624de6448b78e980320a27266557350198dc67cf64bc8561c27e8"
|
||||
checksum = "e236bf5873ea57ec2877445297f4da008916bfae51567131acfc54a073d694f3"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"digest 0.9.0",
|
||||
"either",
|
||||
"futures",
|
||||
"hex 0.4.3",
|
||||
"memmap2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha-1 0.9.8",
|
||||
"sha2 0.9.9",
|
||||
"ssri",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cache-padded"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
@@ -502,6 +290,36 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
|
||||
dependencies = [
|
||||
"clap 4.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_command"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
|
||||
dependencies = [
|
||||
"clap 4.0.22",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_fig"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
||||
dependencies = [
|
||||
"clap 4.0.22",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.21"
|
||||
@@ -582,15 +400,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
|
||||
dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "configparser"
|
||||
version = "3.0.2"
|
||||
@@ -616,15 +425,6 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
@@ -719,16 +519,6 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.81"
|
||||
@@ -785,24 +575,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array 0.14.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "4.0.1"
|
||||
@@ -890,18 +662,6 @@ version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.8.0"
|
||||
@@ -940,15 +700,15 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.128-dev.0"
|
||||
version = "0.0.145-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
"configparser",
|
||||
"fnv",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"ruff",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
@@ -1014,129 +774,6 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"memchr",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
@@ -1162,21 +799,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
name = "globset"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
|
||||
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"aho-corasick",
|
||||
"bstr 0.2.17",
|
||||
"fnv",
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1209,18 +841,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hexf-parse"
|
||||
version = "0.2.1"
|
||||
@@ -1363,15 +983,6 @@ dependencies = [
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.19.8"
|
||||
@@ -1455,7 +1066,7 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
|
||||
dependencies = [
|
||||
"chic",
|
||||
"itertools",
|
||||
@@ -1470,7 +1081,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcst_derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -1508,7 +1119,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1532,15 +1142,6 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
@@ -1727,30 +1328,12 @@ version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
@@ -1923,18 +1506,6 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.4"
|
||||
@@ -1963,20 +1534,6 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"log",
|
||||
"wepoll-ffi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@@ -2248,7 +1805,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.128"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2256,9 +1813,10 @@ dependencies = [
|
||||
"atty",
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"cacache",
|
||||
"cachedir",
|
||||
"chrono",
|
||||
"clap 4.0.22",
|
||||
"clap_complete_command",
|
||||
"clearscreen",
|
||||
"colored",
|
||||
"common-path",
|
||||
@@ -2266,9 +1824,8 @@ dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"fern",
|
||||
"filetime",
|
||||
"fnv",
|
||||
"getrandom 0.2.8",
|
||||
"glob",
|
||||
"globset",
|
||||
"insta",
|
||||
"itertools",
|
||||
"libcst",
|
||||
@@ -2281,6 +1838,7 @@ dependencies = [
|
||||
"rayon",
|
||||
"regex",
|
||||
"ropey",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
"rustpython-common",
|
||||
"rustpython-parser",
|
||||
@@ -2298,7 +1856,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.128"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -2313,6 +1871,12 @@ dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.7"
|
||||
@@ -2328,7 +1892,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2338,7 +1902,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -2361,7 +1925,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2378,7 +1942,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2390,6 +1954,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
"rustpython-compiler-core",
|
||||
"thiserror",
|
||||
@@ -2479,75 +2044,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"fake-simd",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"fake-simd",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.2.0"
|
||||
@@ -2581,37 +2077,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "ssri"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9cec0d388f39fbe79d7aa600e8d38053bf97b1bc8d350da7c0ba800d0f423f2"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"digest 0.8.1",
|
||||
"hex 0.3.2",
|
||||
"serde",
|
||||
"sha-1 0.8.2",
|
||||
"sha2 0.8.2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -2856,12 +2327,6 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "unic-char-property"
|
||||
version = "0.9.0"
|
||||
@@ -3000,7 +2465,7 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"base64",
|
||||
"chunked_transfer",
|
||||
"flate2",
|
||||
"log",
|
||||
@@ -3024,16 +2489,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.0.0-alpha.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
@@ -3055,12 +2510,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "waker-fn"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
@@ -3109,18 +2558,6 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
@@ -3179,15 +2616,6 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-ffi"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.3.0"
|
||||
|
||||
18
Cargo.toml
18
Cargo.toml
@@ -6,8 +6,9 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.128"
|
||||
version = "0.0.145"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
@@ -18,17 +19,18 @@ anyhow = { version = "1.0.66" }
|
||||
atty = { version = "0.2.14" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
cachedir = { version = "0.3.0" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
clap_complete_command = "0.4.0"
|
||||
colored = { version = "2.0.0" }
|
||||
common-path = { version = "1.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
filetime = { version = "0.2.17" }
|
||||
fnv = { version = "1.0.7" }
|
||||
glob = { version = "0.3.0" }
|
||||
globset = {version = "0.4.9" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
log = { version = "0.4.17" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
notify = { version = "4.0.17" }
|
||||
@@ -38,9 +40,10 @@ path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix
|
||||
rayon = { version = "1.5.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
@@ -52,7 +55,6 @@ update-informer = { version = "0.5.0", default-features = false, features = ["py
|
||||
walkdir = { version = "2.3.2" }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
cacache = { version = "10.0.1" } # uses async-std
|
||||
clearscreen = { version = "1.0.10" } # uses which
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
|
||||
75
LICENSE
75
LICENSE
@@ -242,6 +242,56 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-debugger, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Joseph Kahn
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
- flake8-eradicate, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Nikita Sobolev
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
- flake8-tidy-imports, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
@@ -443,3 +493,28 @@ are:
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- RustPython, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 RustPython Team
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
812
README.md
812
README.md
@@ -20,27 +20,48 @@ An extremely fast Python linter, written in Rust.
|
||||
- 🤝 Python 3.10 compatibility
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 `--fix` support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 👀 `--watch` support, for continuous file monitoring
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- ⚖️ [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/pydocstyle/))
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
|
||||
|
||||
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
|
||||
functionality behind a single, common interface. Ruff can be used to replace Flake8 (plus a variety
|
||||
of plugins), [`isort`](https://pypi.org/project/isort/), [`pydocstyle`](https://pypi.org/project/pydocstyle/),
|
||||
[`yesqa`](https://github.com/asottile/yesqa), and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/)
|
||||
and [`autoflake`](https://pypi.org/project/autoflake/) all while executing tens or hundreds of times
|
||||
faster than any individual tool.
|
||||
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
|
||||
and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/) and [`autoflake`](https://pypi.org/project/autoflake/)
|
||||
all while executing tens or hundreds of times faster than any individual tool.
|
||||
|
||||
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
|
||||
automatically convert your existing configuration.)
|
||||
Ruff is extremely actively developed and used in major open-source projects like:
|
||||
|
||||
Ruff is actively developed and used in major open-source projects
|
||||
like [FastAPI](https://github.com/tiangolo/fastapi), [Zulip](https://github.com/zulip/zulip),
|
||||
[pydantic](https://github.com/pydantic/pydantic), and [Saleor](https://github.com/saleor/saleor).
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Zulip](https://github.com/zulip/zulip)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
|
||||
## Testimonials
|
||||
|
||||
[**Sebastián Ramírez**](https://twitter.com/tiangolo/status/1591912354882764802), creator
|
||||
of [FastAPI](https://github.com/tiangolo/fastapi):
|
||||
|
||||
> Ruff is so fast that sometimes I add an intentional bug in the code just to confirm it's actually
|
||||
> running and checking the code.
|
||||
|
||||
[**Bryan Van de Ven**](https://github.com/bokeh/bokeh/pull/12605), co-creator
|
||||
of [Bokeh](https://github.com/bokeh/bokeh/), original author
|
||||
of [Conda](https://docs.conda.io/en/latest/):
|
||||
|
||||
> Ruff is ~150-200x faster than flake8 on my machine, scanning the whole repo takes ~0.2s instead of
|
||||
> ~20s. This is an enormous quality of life improvement for local dev. It's fast enough that I added
|
||||
> it as an actual commit hook, which is terrific.
|
||||
|
||||
[**Tim Abbott**](https://github.com/charliermarsh/ruff/issues/465#issuecomment-1317400028), lead developer of [Zulip](https://github.com/zulip/zulip):
|
||||
|
||||
> This is just ridiculously fast... `ruff` is amazing.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
@@ -52,17 +73,19 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
1. [pydocstyle (D)](#pydocstyle)
|
||||
1. [pyupgrade (U)](#pyupgrade)
|
||||
1. [pep8-naming (N)](#pep8-naming)
|
||||
1. [eradicate (ERA)](#eradicate)
|
||||
1. [flake8-bandit (S)](#flake8-bandit)
|
||||
1. [flake8-comprehensions (C)](#flake8-comprehensions)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
|
||||
1. [flake8-bugbear (B)](#flake8-bugbear)
|
||||
1. [flake8-builtins (A)](#flake8-builtins)
|
||||
1. [flake8-debugger (T)](#flake8-debugger)
|
||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports)
|
||||
1. [flake8-print (T)](#flake8-print)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes)
|
||||
1. [flake8-annotations (ANN)](#flake8-annotations)
|
||||
1. [flake8-2020 (YTT)](#flake8-2020)
|
||||
1. [flake8-blind-except (BLE)](#flake8-blind-except)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
|
||||
1. [mccabe (C90)](#mccabe)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules)
|
||||
1. [Meta rules (M)](#meta-rules)
|
||||
@@ -71,6 +94,7 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
1. [Development](#development)
|
||||
1. [Releases](#releases)
|
||||
1. [Benchmarks](#benchmarks)
|
||||
1. [Reference](#reference)
|
||||
1. [License](#license)
|
||||
1. [Contributing](#contributing)
|
||||
|
||||
@@ -78,12 +102,18 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
|
||||
### Installation
|
||||
|
||||
Available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
|
||||
```shell
|
||||
pip install ruff
|
||||
```
|
||||
|
||||
If you're a **macOS Homebrew** or a **Linuxbrew** user, you can also install `ruff` via Homebrew:
|
||||
|
||||
```shell
|
||||
brew install ruff
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
To run Ruff, try any of the following:
|
||||
@@ -105,7 +135,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.116
|
||||
rev: v0.0.145
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -115,8 +145,10 @@ _Note: prior to `v0.0.86`, `ruff-pre-commit` used `lint` (rather than `ruff`) as
|
||||
|
||||
## Configuration
|
||||
|
||||
Ruff is configurable both via `pyproject.toml` and the command line. If left unspecified, the
|
||||
default configuration is equivalent to:
|
||||
Ruff is configurable both via `pyproject.toml` and the command line. For a full list of configurable
|
||||
options, see the [API reference](#reference).
|
||||
|
||||
If left unspecified, the default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
@@ -155,6 +187,10 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# Assume Python 3.10.
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
# Unlike Flake8, default to a complexity level of 10.
|
||||
max-complexity = 10
|
||||
```
|
||||
|
||||
As an example, the following would configure Ruff to: (1) avoid checking for line-length
|
||||
@@ -173,8 +209,10 @@ ignore = ["E501"]
|
||||
fix = true
|
||||
unfixable = ["F401"]
|
||||
|
||||
# Ignore `E402` (import violations in any `__init__.py` file, and in `path/to/file.py`.
|
||||
per-file-ignores = {"__init__.py" = ["E402"], "path/to/file.py" = ["E402"]}
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[tool.ruff.per-file-ignores]
|
||||
"__init__.py" = ["E402"]
|
||||
"path/to/file.py" = ["E402"]
|
||||
```
|
||||
|
||||
Plugin configurations should be expressed as subsections, e.g.:
|
||||
@@ -188,7 +226,9 @@ select = ["E", "F", "Q"]
|
||||
docstring-quotes = "double"
|
||||
```
|
||||
|
||||
Alternatively, common configuration settings can be provided via the command-line:
|
||||
For a full list of configurable options, see the [API reference](#reference).
|
||||
|
||||
Some common configuration settings can be provided via the command-line:
|
||||
|
||||
```shell
|
||||
ruff path/to/code/ --select F401 --select F403
|
||||
@@ -265,21 +305,10 @@ Options:
|
||||
Print version information
|
||||
```
|
||||
|
||||
### Excluding files
|
||||
|
||||
Exclusions are based on globs, and can be either:
|
||||
|
||||
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
|
||||
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
|
||||
`foo_*.py` ).
|
||||
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
|
||||
(to exclude any Python files in `directory`). Note that these paths are relative to the
|
||||
project root (e.g., the directory containing your `pyproject.toml`).
|
||||
|
||||
### Ignoring errors
|
||||
|
||||
To omit a lint check entirely, add it to the "ignore" list via `--ignore` or `--extend-ignore`,
|
||||
either on the command-line or in your `project.toml` file.
|
||||
either on the command-line or in your `project.toml` file.
|
||||
|
||||
To ignore an error in-line, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
|
||||
To ignore an individual error, add `# noqa: {code}` to the end of the line, like so:
|
||||
@@ -344,6 +373,20 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
| F405 | ImportStarUsage | `...` may be undefined, or defined from star imports: `...` | |
|
||||
| F406 | ImportStarNotPermitted | `from ... import *` only allowed at module level | |
|
||||
| F407 | FutureFeatureNotDefined | Future feature `...` is not defined | |
|
||||
| F501 | PercentFormatInvalidFormat | '...' % ... has invalid format string: ... | |
|
||||
| F502 | PercentFormatExpectedMapping | '...' % ... expected mapping but got sequence | |
|
||||
| F503 | PercentFormatExpectedSequence | '...' % ... expected sequence but got mapping | |
|
||||
| F504 | PercentFormatExtraNamedArguments | '...' % ... has unused named argument(s): ... | |
|
||||
| F505 | PercentFormatMissingArgument | '...' % ... is missing argument(s) for placeholder(s): ... | |
|
||||
| F506 | PercentFormatMixedPositionalAndNamed | '...' % ... has mixed positional and named placeholders | |
|
||||
| F507 | PercentFormatPositionalCountMismatch | '...' % ... has 4 placeholder(s) but 2 substitution(s) | |
|
||||
| F508 | PercentFormatStarRequiresSequence | '...' % ... `*` specifier requires sequence | |
|
||||
| F509 | PercentFormatUnsupportedFormatCharacter | '...' % ... has unsupported format character 'c' | |
|
||||
| F521 | StringDotFormatInvalidFormat | '...'.format(...) has invalid format string: ... | |
|
||||
| F522 | StringDotFormatExtraNamedArguments | '...'.format(...) has unused named argument(s): ... | |
|
||||
| F523 | StringDotFormatExtraPositionalArguments | '...'.format(...) has unused arguments at position(s): ... | |
|
||||
| F524 | StringDotFormatMissingArguments | '...'.format(...) is missing argument(s) for placeholder(s): ... | |
|
||||
| F525 | StringDotFormatMixingAutomatic | '...'.format(...) mixes automatic and manual numbering | |
|
||||
| F541 | FStringMissingPlaceholders | f-string without any placeholders | |
|
||||
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated | |
|
||||
| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated | |
|
||||
@@ -380,7 +423,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
|
||||
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
|
||||
| E722 | DoNotUseBareExcept | Do not use bare `except` | |
|
||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | |
|
||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | 🛠 |
|
||||
| E741 | AmbiguousVariableName | Ambiguous variable name: `...` | |
|
||||
| E742 | AmbiguousClassName | Ambiguous class name: `...` | |
|
||||
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
|
||||
@@ -465,7 +508,9 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| U010 | UnnecessaryFutureImport | Unnecessary `__future__` import `...` for target Python version | 🛠 |
|
||||
| U011 | UnnecessaryLRUCacheParams | Unnecessary parameters to `functools.lru_cache` | 🛠 |
|
||||
| U012 | UnnecessaryEncodeUTF8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
|
||||
| U013 | ConvertTypedDictFunctionalToClass | Convert `TypedDict` functional syntax to class syntax | 🛠 |
|
||||
| U013 | ConvertTypedDictFunctionalToClass | Convert `...` from `TypedDict` functional to class syntax | 🛠 |
|
||||
| U014 | ConvertNamedTupleFunctionalToClass | Convert `...` from `NamedTuple` functional to class syntax | 🛠 |
|
||||
| U015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
|
||||
|
||||
### pep8-naming
|
||||
|
||||
@@ -489,6 +534,14 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
|
||||
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
|
||||
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
|
||||
|
||||
### eradicate
|
||||
|
||||
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ERA001 | CommentedCode | Found commented-out code | 🛠 |
|
||||
|
||||
### flake8-bandit
|
||||
|
||||
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on PyPI.
|
||||
@@ -525,6 +578,14 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
|
||||
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
|
||||
|
||||
### flake8-debugger
|
||||
|
||||
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| T100 | Debugger | Import for `...` found | |
|
||||
|
||||
### flake8-boolean-trap
|
||||
|
||||
For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/0.1.0/) on PyPI.
|
||||
@@ -549,10 +610,10 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body | 🛠 |
|
||||
| B008 | FunctionCallArgumentDefault | Do not perform function call in argument defaults | |
|
||||
| B009 | GetAttrWithConstant | Do not call `getattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
|
||||
| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value. It is not any safer than normal property access. | |
|
||||
| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
|
||||
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
|
||||
| B012 | JumpStatementInFinally | `return/continue/break` inside finally blocks cause exceptions to be silenced | |
|
||||
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`. | |
|
||||
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`. | 🛠 |
|
||||
| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | 🛠 |
|
||||
| B015 | UselessComparison | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. | |
|
||||
| B016 | CannotRaiseLiteral | Cannot raise a literal. Did you intend to return it or raise an Exception? | |
|
||||
@@ -562,10 +623,12 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
| B020 | LoopVariableOverridesIterator | Loop control variable `...` overrides iterable it iterates | |
|
||||
| B021 | FStringDocstring | f-string used as docstring. This will be interpreted by python as a joined string rather than a docstring. | |
|
||||
| B022 | UselessContextlibSuppress | No arguments passed to `contextlib.suppress`. No exceptions will be suppressed and therefore this context manager is redundant | |
|
||||
| B023 | FunctionUsesLoopVariable | Function definition does not bind loop variable `...` | |
|
||||
| B024 | AbstractBaseClassWithoutAbstractMethod | `...` is an abstract base class, but it has no abstract methods | |
|
||||
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | |
|
||||
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged | |
|
||||
| B027 | EmptyMethodWithoutAbstractDecorator | `...` is an empty method in an abstract base class, but has no abstract decorator | |
|
||||
| B904 | RaiseWithoutFromInsideExcept | Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling | |
|
||||
|
||||
### flake8-builtins
|
||||
|
||||
@@ -663,6 +726,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
|
||||
| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
|
||||
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | |
|
||||
| RUF101 | ConvertExitToSysExit | `exit()` is only available in the interpreter, use `sys.exit()` instead | 🛠 |
|
||||
|
||||
### Meta rules
|
||||
|
||||
@@ -793,42 +857,44 @@ stylistic lint rules that are obviated by autoformatting.
|
||||
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
|
||||
automatically convert your existing configuration.)
|
||||
|
||||
Ruff can be used as a (near) drop-in replacement for Flake8 when used (1) without or with a small
|
||||
number of plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
Ruff can be used as a drop-in replacement for Flake8 when used (1) without or with a small number of
|
||||
plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
|
||||
Under those conditions Ruff is missing 14 rules related to string `.format` calls, 1 rule related
|
||||
to docstring parsing, and 1 rule related to redefined variables.
|
||||
Under those conditions, Ruff implements every rule in Flake8, with the exception of `F811`.
|
||||
|
||||
Ruff re-implements some of the most popular Flake8 plugins and related code quality tools natively,
|
||||
including:
|
||||
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||
natively, including:
|
||||
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (25/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
|
||||
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/33)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
- [`eradicate`](https://pypi.org/project/eradicate/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
Beyond the rule set, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
|
||||
1. Ruff does not yet support a few Python 3.9 and 3.10 language features, including structural
|
||||
pattern matching and parenthesized context managers.
|
||||
2. Flake8 has a plugin architecture and supports writing custom lint rules. (To date, popular Flake8
|
||||
plugins have been re-implemented within Ruff directly.)
|
||||
2. Flake8 has a plugin architecture and supports writing custom lint rules. (Instead, popular Flake8
|
||||
plugins are re-implemented in Rust as part of Ruff itself.)
|
||||
|
||||
### Which tools does Ruff replace?
|
||||
|
||||
@@ -836,23 +902,25 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (26/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
|
||||
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/33).
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33).
|
||||
|
||||
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
|
||||
|
||||
@@ -891,7 +959,7 @@ select = [
|
||||
"E",
|
||||
"W",
|
||||
# isort
|
||||
"I"
|
||||
"I001"
|
||||
]
|
||||
src = ["src", "tests"]
|
||||
|
||||
@@ -1130,6 +1198,614 @@ Summary
|
||||
389.73 ± 9.92 times faster than 'flake8 resources/test/cpython'
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
### Options
|
||||
|
||||
#### [`dummy_variable_rgx`](#dummy_variable_rgx)
|
||||
|
||||
A regular expression used to identify "dummy" variables, or those which should be ignored when evaluating
|
||||
(e.g.) unused-variable checks.
|
||||
|
||||
**Default value**: `"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"` (matches `_`, `__`, and `_var`, but not `_var_`)
|
||||
|
||||
**Type**: `Regex`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Only ignore variables named "_".
|
||||
dummy_variable_rgx = "^_$"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`exclude`](#exclude)
|
||||
|
||||
A list of file patterns to exclude from linting.
|
||||
|
||||
Exclusions are based on globs, and can be either:
|
||||
|
||||
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
|
||||
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
|
||||
`foo_*.py` ).
|
||||
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
|
||||
(to exclude any Python files in `directory`). Note that these paths are relative to the
|
||||
project root (e.g., the directory containing your `pyproject.toml`).
|
||||
|
||||
Note that you'll typically want to use [`extend_exclude`](#extend_exclude) to modify the excluded
|
||||
paths.
|
||||
|
||||
**Default value**: `[".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]`
|
||||
|
||||
**Type**: `Vec<FilePattern>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
exclude = [".venv"]
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
#### [`extend_exclude`](#extend_exclude)
|
||||
|
||||
A list of file patterns to omit from linting, in addition to those specified by `exclude`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<FilePattern>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# In addition to the standard set of exclusions, omit all tests, plus a specific file.
|
||||
extend-exclude = ["tests", "src/bad.py"]
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
#### [`ignore`](#ignore)
|
||||
|
||||
A list of check code prefixes to ignore. Prefixes can specify exact checks (like `F841`), entire
|
||||
categories (like `F`), or anything in between.
|
||||
|
||||
When breaking ties between enabled and disabled checks (via `select` and `ignore`, respectively),
|
||||
more specific prefixes override less specific prefixes.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Skip unused variable checks (`F841`).
|
||||
ignore = ["F841"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extend_ignore`](#extend_ignore)
|
||||
|
||||
A list of check code prefixes to ignore, in addition to those specified by `ignore`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Skip unused variable checks (`F841`).
|
||||
extend-ignore = ["F841"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`select`](#select)
|
||||
|
||||
A list of check code prefixes to enable. Prefixes can specify exact checks (like `F841`), entire
|
||||
categories (like `F`), or anything in between.
|
||||
|
||||
When breaking ties between enabled and disabled checks (via `select` and `ignore`, respectively),
|
||||
more specific prefixes override less specific prefixes.
|
||||
|
||||
**Default value**: `["E", "F"]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# On top of the defaults (`E`, `F`), enable flake8-bugbear (`B`) and flake8-quotes (`Q`).
|
||||
select = ["E", "F", "B", "Q"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extend_select`](#extend_select)
|
||||
|
||||
A list of check code prefixes to enable, in addition to those specified by `select`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# On top of the default `select` (`E`, `F`), enable flake8-bugbear (`B`) and flake8-quotes (`Q`).
|
||||
extend-select = ["B", "Q"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`fix`](#fix)
|
||||
|
||||
Enable autofix behavior by-default when running `ruff` (overridden by the `--fix` and `--no-fix`
|
||||
command-line flags).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
fix = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`fixable`](#fixable)
|
||||
|
||||
A list of check code prefixes to consider autofix-able.
|
||||
|
||||
**Default value**: `["A", "ANN", "B", "BLE", "C", "D", "E", "F", "FBT", "I", "M", "N", "Q", "RUF", "S", "T", "U", "W", "YTT"]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Only allow autofix behavior for `E` and `F` checks.
|
||||
fixable = ["E", "F"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`unfixable`](#unfixable)
|
||||
|
||||
A list of check code prefixes to consider un-autofix-able.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Disable autofix for unused imports (`F401`).
|
||||
unfixable = ["F401"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`line_length`](#line_length)
|
||||
|
||||
The line length to use when enforcing long-lines violations (like E501).
|
||||
|
||||
**Default value**: `88`
|
||||
|
||||
**Type**: `usize`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Allow lines to be as long as 120 characters.
|
||||
line-length = 120
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`per_file_ignores`](#per_file_ignores)
|
||||
|
||||
A list of mappings from file pattern to check code prefixes to exclude, when considering any
|
||||
matching files.
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
**Type**: `HashMap<String, Vec<CheckCodePrefix>>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[tool.ruff.per-file-ignores]
|
||||
"__init__.py" = ["E402"]
|
||||
"path/to/file.py" = ["E402"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`show_source`](#show_source)
|
||||
|
||||
Whether to show source code snippets when reporting lint error violations (overridden by the
|
||||
`--show-source` command-line flag).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# By default, always show source code snippets.
|
||||
show_source = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`src`](#src)
|
||||
|
||||
The source code paths to consider, e.g., when resolving first- vs. third-party imports.
|
||||
|
||||
**Default value**: `["."]`
|
||||
|
||||
**Type**: `Vec<PathBuf>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Allow imports relative to the "src" and "test" directories.
|
||||
src = ["src", "test"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`target_version`](#target_version)
|
||||
|
||||
The Python version to target, e.g., when considering automatic code upgrades, like rewriting type
|
||||
annotations. Note that the target version will _not_ be inferred from the _current_ Python version,
|
||||
and instead must be specified explicitly (as seen below).
|
||||
|
||||
**Default value**: `"py310"`
|
||||
|
||||
**Type**: `PythonVersion`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Always generate Python 3.7-compatible code.
|
||||
target-version = "py37"
|
||||
```
|
||||
|
||||
### `flake8-annotations`
|
||||
|
||||
#### [`mypy_init_return`](#mypy_init_return)
|
||||
|
||||
Whether to allow the omission of a return type hint for `__init__` if at least one argument is
|
||||
annotated.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
mypy_init_return = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`suppress_dummy_args`](#suppress_dummy_args)
|
||||
|
||||
Whether to suppress `ANN000`-level errors for arguments matching the "dummy" variable regex (like
|
||||
`_`).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
suppress_dummy_args = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`suppress_none_returning`](#suppress_none_returning)
|
||||
|
||||
Whether to suppress `ANN200`-level errors for functions that meet either of the following criteria:
|
||||
|
||||
- Contain no `return` statement.
|
||||
- Explicit `return` statement(s) all return `None` (explicitly or implicitly).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
suppress_none_returning = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`allow_star_arg_any`](#allow_star_arg_any)
|
||||
|
||||
Whether to suppress `ANN401` for dynamically typed `*args` and `**kwargs` arguments.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
allow_star_arg_any = true
|
||||
```
|
||||
|
||||
### `flake8-bugbear`
|
||||
|
||||
#### [`extend_immutable_calls`](#extend_immutable_calls)
|
||||
|
||||
Additional callable functions to consider "immutable" when evaluating, e.g., no-mutable-default-argument
|
||||
checks (`B006`).
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-bugbear]
|
||||
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
|
||||
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
```
|
||||
|
||||
### `flake8-quotes`
|
||||
|
||||
#### [`inline_quotes`](#inline_quotes)
|
||||
|
||||
Quote style to prefer for inline strings (either "single" (`'`) or "double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
**Type**: `Quote`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`multiline_quotes`](#multiline_quotes)
|
||||
|
||||
Quote style to prefer for multiline strings (either "single" (`'`) or "double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
**Type**: `Quote`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
multiline-quotes = "single"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`docstring_quotes`](#docstring_quotes)
|
||||
|
||||
Quote style to prefer for docstrings (either "single" (`'`) or "double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
**Type**: `Quote`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
docstring-quotes = "single"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`avoid_escape`](#avoid_escape)
|
||||
|
||||
Whether to avoid using single quotes if a string contains single quotes, or vice-versa with
|
||||
double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes). This minimizes the
|
||||
need to escape quotation marks within strings.
|
||||
|
||||
**Default value**: `true`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
# Don't bother trying to avoid escapes.
|
||||
avoid-escape = false
|
||||
```
|
||||
|
||||
### `flake8-tidy-imports`
|
||||
|
||||
#### [`ban_relative_imports`](#ban_relative_imports)
|
||||
|
||||
Whether to ban all relative imports (`"all"`), or only those imports that extend into the parent
|
||||
module and beyond (`"parents"`).
|
||||
|
||||
**Default value**: `"parents"`
|
||||
|
||||
**Type**: `Strictness`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
# Disallow all relative imports.
|
||||
ban-relative-imports = "all"
|
||||
```
|
||||
|
||||
### `isort`
|
||||
|
||||
#### [`known_first_party`](known_first_party)
|
||||
|
||||
A list of modules to consider first-party, regardless of whether they can be identified as such
|
||||
via introspection of the local filesystem.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["src"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`known_third_party`](known_third_party)
|
||||
|
||||
A list of modules to consider third-party, regardless of whether they can be identified as such
|
||||
via introspection of the local filesystem.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
known-third-party = ["fastapi"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extra_standard_library`](extra_standard_library)
|
||||
|
||||
A list of modules to consider standard-library, in addition to those known to Ruff in advance.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
extra-standard-library = ["path"]
|
||||
```
|
||||
|
||||
### `mccabe`
|
||||
|
||||
#### [`max_complexity`](#max_complexity)
|
||||
|
||||
The maximum McCabe complexity to allow before triggering `C901` errors.
|
||||
|
||||
**Default value**: `10`
|
||||
|
||||
**Type**: `usize`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
# Flag errors (`C901`) whenever the complexity level exceeds 5.
|
||||
max-complexity = 5
|
||||
```
|
||||
|
||||
### `pep8-naming`
|
||||
|
||||
#### [`ignore_names`](#ignore_names)
|
||||
|
||||
A list of names to ignore when considering `pep8-naming` violations.
|
||||
|
||||
**Default value**: `["setUp", "tearDown", "setUpClass", "tearDownClass", "setUpModule", "tearDownModule", "asyncSetUp", "asyncTearDown", "setUpTestData", "failureException", "longMessage", "maxDiff"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pep8-naming]
|
||||
ignore-names = ["callMethod"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`classmethod_decorators`](#classmethod_decorators)
|
||||
|
||||
A list of decorators that, when applied to a method, indicate that the method should be treated as
|
||||
a class method. For example, Ruff will expect that any method decorated by a decorator in this list
|
||||
takes a `cls` argument as its first argument.
|
||||
|
||||
**Default value**: `["classmethod"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pep8-naming]
|
||||
# Allow Pydantic's `@validator` decorator to trigger class method treatment.
|
||||
classmethod-decorators = ["classmethod", "pydantic.validator"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`staticmethod_decorators`](#staticmethod_decorators)
|
||||
|
||||
A list of decorators that, when applied to a method, indicate that the method should be treated as
|
||||
a static method. For example, Ruff will expect that any method decorated by a decorator in this list
|
||||
has no `self` or `cls` argument.
|
||||
|
||||
**Default value**: `["staticmethod"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pep8-naming]
|
||||
# Allow a shorthand alias, `@stcmthd`, to trigger static method treatment.
|
||||
staticmethod-decorators = ["staticmethod", "stcmthd"]
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -10,7 +10,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
b.iter(|| {
|
||||
let rope = Rope::from_str(black_box(&contents));
|
||||
rope.line_to_char(black_box(4));
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
16
flake8_to_ruff/Cargo.lock
generated
16
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.128"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1265,7 +1265,7 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
|
||||
dependencies = [
|
||||
"chic",
|
||||
"itertools",
|
||||
@@ -1280,7 +1280,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcst_derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
|
||||
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.128"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@@ -2028,7 +2028,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
@@ -2038,7 +2038,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -2061,7 +2061,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2078,7 +2078,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.128-dev.0"
|
||||
version = "0.0.145-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@@ -10,10 +10,10 @@ name = "flake8_to_ruff"
|
||||
anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
configparser = { version = "3.0.2" }
|
||||
fnv = { version = "1.0.7" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff = { path = "..", default-features = false }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
toml = { version = "0.5.9" }
|
||||
|
||||
@@ -25,7 +25,7 @@ requires-python = ">=3.7"
|
||||
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=0.13,<0.14"]
|
||||
requires = ["maturin>=0.14,<0.15"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[tool.maturin]
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn convert(
|
||||
plugins: Option<Vec<Plugin>>,
|
||||
) -> Result<Pyproject> {
|
||||
// Extract all referenced check code prefixes, to power plugin inference.
|
||||
let mut referenced_codes: BTreeSet<CheckCodePrefix> = Default::default();
|
||||
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
|
||||
for (key, value) in flake8 {
|
||||
if let Some(value) = value {
|
||||
match key.as_str() {
|
||||
@@ -70,13 +70,13 @@ pub fn convert(
|
||||
.unwrap_or_default();
|
||||
|
||||
// Parse each supported option.
|
||||
let mut options: Options = Default::default();
|
||||
let mut flake8_annotations: flake8_annotations::settings::Options = Default::default();
|
||||
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
|
||||
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
|
||||
let mut flake8_tidy_imports: flake8_tidy_imports::settings::Options = Default::default();
|
||||
let mut mccabe: mccabe::settings::Options = Default::default();
|
||||
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
|
||||
let mut options = Options::default();
|
||||
let mut flake8_annotations = flake8_annotations::settings::Options::default();
|
||||
let mut flake8_bugbear = flake8_bugbear::settings::Options::default();
|
||||
let mut flake8_quotes = flake8_quotes::settings::Options::default();
|
||||
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
|
||||
let mut mccabe = mccabe::settings::Options::default();
|
||||
let mut pep8_naming = pep8_naming::settings::Options::default();
|
||||
for (key, value) in flake8 {
|
||||
if let Some(value) = value {
|
||||
match key.as_str() {
|
||||
@@ -110,7 +110,7 @@ pub fn convert(
|
||||
match parser::parse_files_to_codes_mapping(value.as_ref()) {
|
||||
Ok(per_file_ignores) => {
|
||||
options.per_file_ignores =
|
||||
Some(parser::collect_per_file_ignores(per_file_ignores))
|
||||
Some(parser::collect_per_file_ignores(per_file_ignores));
|
||||
}
|
||||
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||
}
|
||||
@@ -181,7 +181,7 @@ pub fn convert(
|
||||
"ban-relative-imports" | "ban_relative_imports" => match value.trim() {
|
||||
"true" => flake8_tidy_imports.ban_relative_imports = Some(Strictness::All),
|
||||
"parents" => {
|
||||
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents)
|
||||
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents);
|
||||
}
|
||||
_ => eprintln!("Unexpected '{key}' value: {value}"),
|
||||
},
|
||||
@@ -203,22 +203,22 @@ pub fn convert(
|
||||
// Deduplicate and sort.
|
||||
options.select = Some(Vec::from_iter(select));
|
||||
options.ignore = Some(Vec::from_iter(ignore));
|
||||
if flake8_annotations != Default::default() {
|
||||
if flake8_annotations != flake8_annotations::settings::Options::default() {
|
||||
options.flake8_annotations = Some(flake8_annotations);
|
||||
}
|
||||
if flake8_bugbear != Default::default() {
|
||||
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
|
||||
options.flake8_bugbear = Some(flake8_bugbear);
|
||||
}
|
||||
if flake8_quotes != Default::default() {
|
||||
if flake8_quotes != flake8_quotes::settings::Options::default() {
|
||||
options.flake8_quotes = Some(flake8_quotes);
|
||||
}
|
||||
if flake8_tidy_imports != Default::default() {
|
||||
if flake8_tidy_imports != flake8_tidy_imports::settings::Options::default() {
|
||||
options.flake8_tidy_imports = Some(flake8_tidy_imports);
|
||||
}
|
||||
if mccabe != Default::default() {
|
||||
if mccabe != mccabe::settings::Options::default() {
|
||||
options.mccabe = Some(mccabe);
|
||||
}
|
||||
if pep8_naming != Default::default() {
|
||||
if pep8_naming != pep8_naming::settings::Options::default() {
|
||||
options.pep8_naming = Some(pep8_naming);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
#![allow(clippy::collapsible_if, clippy::collapsible_else_if)]
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
pub mod converter;
|
||||
mod parser;
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
//! Utility to generate Ruff's pyproject.toml section from a Flake8 INI file.
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use fnv::FnvHashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use ruff::checks_gen::CheckCodePrefix;
|
||||
use ruff::settings::types::PatternPrefixPair;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
static COMMA_SEPARATED_LIST_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn parse_prefix_codes(value: &str) -> Vec<CheckCodePrefix> {
|
||||
pub fn parse_strings(value: &str) -> Vec<String> {
|
||||
COMMA_SEPARATED_LIST_RE
|
||||
.split(value)
|
||||
.map(|part| part.trim())
|
||||
.map(str::trim)
|
||||
.filter(|part| !part.is_empty())
|
||||
.map(String::from)
|
||||
.collect()
|
||||
@@ -92,7 +92,7 @@ impl State {
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(_) => eprintln!("Skipping unrecognized prefix: {}", code),
|
||||
Err(_) => eprintln!("Skipping unrecognized prefix: {code}"),
|
||||
}
|
||||
}
|
||||
codes
|
||||
@@ -129,14 +129,14 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
|
||||
}
|
||||
tokens.push(Token {
|
||||
token_name: TokenType::Eof,
|
||||
src: "".to_string(),
|
||||
src: String::new(),
|
||||
});
|
||||
tokens
|
||||
}
|
||||
|
||||
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
|
||||
///
|
||||
/// See: https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45
|
||||
/// See: <https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45>
|
||||
pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair>> {
|
||||
if value.trim().is_empty() {
|
||||
return Ok(vec![]);
|
||||
@@ -179,8 +179,8 @@ pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair
|
||||
/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`.
|
||||
pub fn collect_per_file_ignores(
|
||||
pairs: Vec<PatternPrefixPair>,
|
||||
) -> FnvHashMap<String, Vec<CheckCodePrefix>> {
|
||||
let mut per_file_ignores: FnvHashMap<String, Vec<CheckCodePrefix>> = FnvHashMap::default();
|
||||
) -> FxHashMap<String, Vec<CheckCodePrefix>> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
|
||||
for pair in pairs {
|
||||
per_file_ignores
|
||||
.entry(pair.pattern)
|
||||
|
||||
@@ -6,17 +6,19 @@ use ruff::checks_gen::CheckCodePrefix;
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Plugin {
|
||||
Flake8Annotations,
|
||||
Flake8Bandit,
|
||||
Flake8BlindExcept,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
Flake8Comprehensions,
|
||||
Flake8Debugger,
|
||||
Flake8Docstrings,
|
||||
Flake8TidyImports,
|
||||
Flake8Eradicate,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Annotations,
|
||||
Flake8TidyImports,
|
||||
McCabe,
|
||||
Flake8BlindExcept,
|
||||
PEP8Naming,
|
||||
Pyupgrade,
|
||||
}
|
||||
@@ -26,16 +28,18 @@ impl FromStr for Plugin {
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||
match string {
|
||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||
"flake8-bandit" => Ok(Plugin::Flake8Bandit),
|
||||
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
|
||||
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
|
||||
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
|
||||
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
|
||||
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
|
||||
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
|
||||
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
|
||||
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
|
||||
"flake8-print" => Ok(Plugin::Flake8Print),
|
||||
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
|
||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
|
||||
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
|
||||
"mccabe" => Ok(Plugin::McCabe),
|
||||
"pep8-naming" => Ok(Plugin::PEP8Naming),
|
||||
"pyupgrade" => Ok(Plugin::Pyupgrade),
|
||||
@@ -47,16 +51,18 @@ impl FromStr for Plugin {
|
||||
impl Plugin {
|
||||
pub fn default(&self) -> CheckCodePrefix {
|
||||
match self {
|
||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||
Plugin::Flake8Bandit => CheckCodePrefix::S,
|
||||
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
|
||||
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
||||
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
|
||||
Plugin::Flake8Debugger => CheckCodePrefix::T1,
|
||||
Plugin::Flake8Docstrings => CheckCodePrefix::D,
|
||||
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
||||
Plugin::Flake8Print => CheckCodePrefix::T,
|
||||
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
|
||||
Plugin::Flake8Print => CheckCodePrefix::T2,
|
||||
Plugin::Flake8Quotes => CheckCodePrefix::Q,
|
||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
|
||||
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
||||
Plugin::McCabe => CheckCodePrefix::C9,
|
||||
Plugin::PEP8Naming => CheckCodePrefix::N,
|
||||
Plugin::Pyupgrade => CheckCodePrefix::U,
|
||||
@@ -65,10 +71,13 @@ impl Plugin {
|
||||
|
||||
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
|
||||
match self {
|
||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
|
||||
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
|
||||
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
|
||||
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
|
||||
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
|
||||
Plugin::Flake8Docstrings => {
|
||||
// Use the user-provided docstring.
|
||||
for key in ["docstring-convention", "docstring_convention"] {
|
||||
@@ -85,11 +94,10 @@ impl Plugin {
|
||||
// Default to PEP8.
|
||||
DocstringConvention::PEP8.select()
|
||||
}
|
||||
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
Plugin::Flake8Print => vec![CheckCodePrefix::T],
|
||||
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
|
||||
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
|
||||
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
|
||||
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
Plugin::McCabe => vec![CheckCodePrefix::C9],
|
||||
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
||||
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
||||
@@ -277,31 +285,6 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
let mut plugins = BTreeSet::new();
|
||||
for key in flake8.keys() {
|
||||
match key.as_str() {
|
||||
// flake8-docstrings
|
||||
"docstring-convention" | "docstring_convention" => {
|
||||
plugins.insert(Plugin::Flake8Docstrings);
|
||||
}
|
||||
// flake8-bugbear
|
||||
"extend-immutable-calls" | "extend_immutable_calls" => {
|
||||
plugins.insert(Plugin::Flake8Bugbear);
|
||||
}
|
||||
// flake8-builtins
|
||||
"builtins-ignorelist" | "builtins_ignorelist" => {
|
||||
plugins.insert(Plugin::Flake8Builtins);
|
||||
}
|
||||
// flake8-quotes
|
||||
"quotes" | "inline-quotes" | "inline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"multiline-quotes" | "multiline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"docstring-quotes" | "docstring_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"avoid-escape" | "avoid_escape" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
// flake8-annotations
|
||||
"suppress-none-returning" | "suppress_none_returning" => {
|
||||
plugins.insert(Plugin::Flake8Annotations);
|
||||
@@ -327,6 +310,41 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
"allow-star-arg-any" | "allow_star_arg_any" => {
|
||||
plugins.insert(Plugin::Flake8Annotations);
|
||||
}
|
||||
// flake8-bugbear
|
||||
"extend-immutable-calls" | "extend_immutable_calls" => {
|
||||
plugins.insert(Plugin::Flake8Bugbear);
|
||||
}
|
||||
// flake8-builtins
|
||||
"builtins-ignorelist" | "builtins_ignorelist" => {
|
||||
plugins.insert(Plugin::Flake8Builtins);
|
||||
}
|
||||
// flake8-docstrings
|
||||
"docstring-convention" | "docstring_convention" => {
|
||||
plugins.insert(Plugin::Flake8Docstrings);
|
||||
}
|
||||
// flake8-eradicate
|
||||
"eradicate-aggressive" | "eradicate_aggressive" => {
|
||||
plugins.insert(Plugin::Flake8Eradicate);
|
||||
}
|
||||
"eradicate-whitelist" | "eradicate_whitelist" => {
|
||||
plugins.insert(Plugin::Flake8Eradicate);
|
||||
}
|
||||
"eradicate-whitelist-extend" | "eradicate_whitelist_extend" => {
|
||||
plugins.insert(Plugin::Flake8Eradicate);
|
||||
}
|
||||
// flake8-quotes
|
||||
"quotes" | "inline-quotes" | "inline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"multiline-quotes" | "multiline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"docstring-quotes" | "docstring_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"avoid-escape" | "avoid_escape" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
// flake8-tidy-imports
|
||||
"ban-relative-imports" | "ban_relative_imports" => {
|
||||
plugins.insert(Plugin::Flake8TidyImports);
|
||||
@@ -360,16 +378,18 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
/// `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
|
||||
[
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::Flake8Bandit,
|
||||
Plugin::Flake8BlindExcept,
|
||||
Plugin::Flake8Bugbear,
|
||||
Plugin::Flake8Builtins,
|
||||
Plugin::Flake8Comprehensions,
|
||||
Plugin::Flake8Debugger,
|
||||
Plugin::Flake8Docstrings,
|
||||
Plugin::Flake8TidyImports,
|
||||
Plugin::Flake8Eradicate,
|
||||
Plugin::Flake8Print,
|
||||
Plugin::Flake8Quotes,
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::Flake8BlindExcept,
|
||||
Plugin::Flake8TidyImports,
|
||||
Plugin::PEP8Naming,
|
||||
Plugin::Pyupgrade,
|
||||
]
|
||||
|
||||
@@ -25,12 +25,11 @@ requires-python = ">=3.7"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=0.13,<0.14"]
|
||||
requires = ["maturin>=0.14,<0.15"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[tool.maturin]
|
||||
bindings = "bin"
|
||||
sdist-include = ["Cargo.lock"]
|
||||
strip = true
|
||||
|
||||
[tool.isort]
|
||||
|
||||
20
resources/test/fixtures/B006_B008.py
vendored
20
resources/test/fixtures/B006_B008.py
vendored
@@ -185,3 +185,23 @@ def nested_b008(a=random.randint(0, dt.datetime.now().year)):
|
||||
# Ignore lambda contents since they are evaluated at call time.
|
||||
def foo(f=lambda x: print(x)):
|
||||
f(1)
|
||||
|
||||
|
||||
from collections import abc
|
||||
from typing import Annotated, Dict, Optional, Sequence, Union, Set
|
||||
|
||||
|
||||
def immutable_annotations(
|
||||
a: Sequence[int] | None = [],
|
||||
b: Optional[abc.Mapping[int, int]] = {},
|
||||
c: Annotated[Union[abc.Set[str], abc.Sized], "annotation"] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def mutable_annotations(
|
||||
a: list[int] | None = [],
|
||||
b: Optional[Dict[int, int]] = {},
|
||||
c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
1
resources/test/fixtures/B009_B010.py
vendored
1
resources/test/fixtures/B009_B010.py
vendored
@@ -34,3 +34,4 @@ setattr(foo, "bar", None)
|
||||
setattr(foo, "_123abc", None)
|
||||
setattr(foo, "abc123", None)
|
||||
setattr(foo, r"abc123", None)
|
||||
setattr(foo.bar, r"baz", None)
|
||||
|
||||
82
resources/test/fixtures/B023.py
vendored
Normal file
82
resources/test/fixtures/B023.py
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
Should emit:
|
||||
B023 - on lines 12, 13, 16, 28, 29, 30, 31, 40, 42, 50, 51, 52, 53, 61, 68.
|
||||
"""
|
||||
|
||||
functions = []
|
||||
z = 0
|
||||
|
||||
for x in range(3):
|
||||
y = x + 1
|
||||
# Subject to late-binding problems
|
||||
functions.append(lambda: x)
|
||||
functions.append(lambda: y) # not just the loop var
|
||||
|
||||
def f_bad_1():
|
||||
return x
|
||||
|
||||
# Actually OK
|
||||
functions.append(lambda x: x * 2)
|
||||
functions.append(lambda x=x: x)
|
||||
functions.append(lambda: z) # OK because not assigned in the loop
|
||||
|
||||
def f_ok_1(x):
|
||||
return x * 2
|
||||
|
||||
|
||||
def check_inside_functions_too():
|
||||
ls = [lambda: x for x in range(2)]
|
||||
st = {lambda: x for x in range(2)}
|
||||
gn = (lambda: x for x in range(2))
|
||||
dt = {x: lambda: x for x in range(2)}
|
||||
|
||||
|
||||
async def pointless_async_iterable():
|
||||
yield 1
|
||||
|
||||
|
||||
async def container_for_problems():
|
||||
async for x in pointless_async_iterable():
|
||||
functions.append(lambda: x)
|
||||
|
||||
[lambda: x async for x in pointless_async_iterable()]
|
||||
|
||||
|
||||
a = 10
|
||||
b = 0
|
||||
while True:
|
||||
a = a_ = a - 1
|
||||
b += 1
|
||||
functions.append(lambda: a)
|
||||
functions.append(lambda: a_)
|
||||
functions.append(lambda: b)
|
||||
functions.append(lambda: c) # not a name error because of late binding!
|
||||
c: bool = a > 3
|
||||
if not c:
|
||||
break
|
||||
|
||||
# Nested loops should not duplicate reports
|
||||
for j in range(2):
|
||||
for k in range(3):
|
||||
lambda: j * k
|
||||
|
||||
|
||||
for j, k, l in [(1, 2, 3)]:
|
||||
|
||||
def f():
|
||||
j = None # OK because it's an assignment
|
||||
[l for k in range(2)] # error for l, not for k
|
||||
|
||||
assert a and functions
|
||||
|
||||
a.attribute = 1 # modifying an attribute doesn't make it a loop variable
|
||||
functions[0] = lambda: None # same for an element
|
||||
|
||||
for var in range(2):
|
||||
|
||||
def explicit_capture(captured=var):
|
||||
return captured
|
||||
|
||||
|
||||
for i in range(3):
|
||||
lambda: f"{i}"
|
||||
55
resources/test/fixtures/B904.py
vendored
Normal file
55
resources/test/fixtures/B904.py
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
Should emit:
|
||||
B904 - on lines 10, 11 and 16
|
||||
"""
|
||||
|
||||
try:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
if "abc":
|
||||
raise TypeError
|
||||
raise UserWarning
|
||||
except AssertionError:
|
||||
raise # Bare `raise` should not be an error
|
||||
except Exception as err:
|
||||
assert err
|
||||
raise Exception("No cause here...")
|
||||
except BaseException as base_err:
|
||||
# Might use this instead of bare raise with the `.with_traceback()` method
|
||||
raise base_err
|
||||
finally:
|
||||
raise Exception("Nothing to chain from, so no warning here")
|
||||
|
||||
try:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
# should not emit, since we are not raising something
|
||||
def proxy():
|
||||
raise NameError
|
||||
|
||||
|
||||
try:
|
||||
from preferred_library import Thing
|
||||
except ImportError:
|
||||
try:
|
||||
from fallback_library import Thing
|
||||
except ImportError:
|
||||
|
||||
class Thing:
|
||||
def __getattr__(self, name):
|
||||
# same as the case above, should not emit.
|
||||
raise AttributeError
|
||||
|
||||
|
||||
try:
|
||||
from preferred_library import Thing
|
||||
except ImportError:
|
||||
try:
|
||||
from fallback_library import Thing
|
||||
except ImportError:
|
||||
|
||||
def context_switch():
|
||||
try:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
raise
|
||||
30
resources/test/fixtures/C901.py
vendored
30
resources/test/fixtures/C901.py
vendored
@@ -106,3 +106,33 @@ async def foobar(a, b, c):
|
||||
# Complexity = 1
|
||||
def annotated_assign():
|
||||
x: Any = None
|
||||
|
||||
|
||||
# Complexity = 9
|
||||
class Class:
|
||||
def handle(self, *args, **options):
|
||||
if args:
|
||||
return
|
||||
|
||||
class ServiceProvider:
|
||||
def a(self):
|
||||
pass
|
||||
|
||||
def b(self, data):
|
||||
if not args:
|
||||
pass
|
||||
|
||||
class Logger:
|
||||
def c(*args, **kwargs):
|
||||
pass
|
||||
|
||||
def error(self, message):
|
||||
pass
|
||||
|
||||
def info(self, message):
|
||||
pass
|
||||
|
||||
def exception(self):
|
||||
pass
|
||||
|
||||
return ServiceProvider(Logger())
|
||||
|
||||
7
resources/test/fixtures/D.py
vendored
7
resources/test/fixtures/D.py
vendored
@@ -3,6 +3,7 @@ from functools import wraps
|
||||
import os
|
||||
from .expected import Expectation
|
||||
from typing import overload
|
||||
from typing_extensions import override
|
||||
|
||||
|
||||
expectation = Expectation()
|
||||
@@ -42,9 +43,13 @@ class class_:
|
||||
"D418: Function/ Method decorated with @overload"
|
||||
" shouldn't contain a docstring")
|
||||
|
||||
@override
|
||||
def overridden_method(a):
|
||||
return str(a)
|
||||
|
||||
@property
|
||||
def foo(self):
|
||||
"""The foo of the thing, which isn't in imperitive mood."""
|
||||
"""The foo of the thing, which isn't in imperative mood."""
|
||||
return "hello"
|
||||
|
||||
@expect('D102: Missing docstring in public method')
|
||||
|
||||
6
resources/test/fixtures/E501.py
vendored
6
resources/test/fixtures/E501.py
vendored
@@ -49,3 +49,9 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
|
||||
sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
""", # noqa: E501
|
||||
}
|
||||
|
||||
# OK
|
||||
# A very long URL: https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
|
||||
|
||||
# OK
|
||||
# https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
|
||||
|
||||
13
resources/test/fixtures/ERA001.py
vendored
Normal file
13
resources/test/fixtures/ERA001.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#import os
|
||||
# from foo import junk
|
||||
#a = 3
|
||||
a = 4
|
||||
#foo(1, 2, 3)
|
||||
|
||||
def foo(x, y, z):
|
||||
contentet = 1 # print('hello')
|
||||
print(x, y, z)
|
||||
|
||||
# This is a real comment.
|
||||
#return True
|
||||
return False
|
||||
13
resources/test/fixtures/F502.py
vendored
Normal file
13
resources/test/fixtures/F502.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
dog = {"bob": "bob"}
|
||||
|
||||
"%(bob)s" % dog
|
||||
"%(bob)s" % {"bob": "bob"}
|
||||
"%(bob)s" % {**{"bob": "bob"}}
|
||||
"%(bob)s" % ["bob"] # F202
|
||||
"%(bob)s" % ("bob",) # F202
|
||||
"%(bob)s" % {"bob"} # F202
|
||||
"%(bob)s" % [*["bob"]] # F202
|
||||
"%(bob)s" % {"bob": "bob" for _ in range(1)}
|
||||
"%(bob)s" % ["bob" for _ in range(1)] # F202
|
||||
"%(bob)s" % ("bob" for _ in range(1)) # F202
|
||||
"%(bob)s" % {"bob" for _ in range(1)} # F202
|
||||
26
resources/test/fixtures/F503.py
vendored
Normal file
26
resources/test/fixtures/F503.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
dog = {"bob": "bob"}
|
||||
|
||||
# Single placeholder always fine
|
||||
"%s" % dog
|
||||
"%s" % {"bob": "bob"}
|
||||
"%s" % {**{"bob": "bob"}}
|
||||
"%s" % ["bob"]
|
||||
"%s" % ("bob",)
|
||||
"%s" % {"bob"}
|
||||
"%s" % [*["bob"]]
|
||||
"%s" % {"bob": "bob" for _ in range(1)}
|
||||
"%s" % ["bob" for _ in range(1)]
|
||||
"%s" % ("bob" for _ in range(1))
|
||||
"%s" % {"bob" for _ in range(1)}
|
||||
# Multiple placeholders
|
||||
"%s %s" % dog
|
||||
"%s %s" % {"bob": "bob"} # F503
|
||||
"%s %s" % {**{"bob": "bob"}} # F503
|
||||
"%s %s" % ["bob"]
|
||||
"%s %s" % ("bob",)
|
||||
"%s %s" % {"bob"}
|
||||
"%s %s" % [*["bob"]]
|
||||
"%s %s" % {"bob": "bob" for _ in range(1)} # F503
|
||||
"%s %s" % ["bob" for _ in range(1)]
|
||||
"%s %s" % ("bob" for _ in range(1))
|
||||
"%s %s" % {"bob" for _ in range(1)}
|
||||
6
resources/test/fixtures/F504.py
vendored
Normal file
6
resources/test/fixtures/F504.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Ruff has no way of knowing if the following are F505s
|
||||
a = "wrong"
|
||||
"%(a)s %(c)s" % {a: "?", "b": "!"} # F504 ("b" not used)
|
||||
|
||||
hidden = {"a": "!"}
|
||||
"%(a)s %(c)s" % {"x": 1, **hidden} # Ok (cannot see through splat)
|
||||
25
resources/test/fixtures/F50x.py
vendored
Normal file
25
resources/test/fixtures/F50x.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
'%(foo)' % {'foo': 'bar'} # F501
|
||||
'%s %(foo)s' % {'foo': 'bar'} # F506
|
||||
'%(foo)s %s' % {'foo': 'bar'} # F506
|
||||
'%j' % (1,) # F509
|
||||
'%s %s' % (1,) # F507
|
||||
'%s %s' % (1, 2, 3) # F507
|
||||
'%(bar)s' % {} # F505
|
||||
'%(bar)s' % {'bar': 1, 'baz': 2} # F504
|
||||
'%(bar)s' % (1, 2, 3) # F502
|
||||
'%s %s' % {'k': 'v'} # F503
|
||||
'%(bar)*s' % {'bar': 'baz'} # F506, F508
|
||||
|
||||
# ok: single %s with mapping
|
||||
'%s' % {'foo': 'bar', 'baz': 'womp'}
|
||||
# ok: %% should not count towards placeholder count
|
||||
'%% %s %% %s' % (1, 2)
|
||||
# ok: * consumes one positional argument
|
||||
'%.*f' % (2, 1.1234)
|
||||
'%*.*f' % (5, 2, 3.1234)
|
||||
# ok *args and **kwargs
|
||||
a = []
|
||||
'%s %s' % [*a]
|
||||
'%s %s' % (*a,)
|
||||
k = {}
|
||||
'%(k)s' % {**k}
|
||||
21
resources/test/fixtures/F521.py
vendored
Normal file
21
resources/test/fixtures/F521.py
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
"{".format(1)
|
||||
"}".format(1)
|
||||
"{foo[}".format(foo=1)
|
||||
# too much string recursion (placeholder-in-placeholder)
|
||||
"{:{:{}}}".format(1, 2, 3)
|
||||
# ruff picks these issues up, but flake8 doesn't
|
||||
"{foo[]}".format(foo={"": 1})
|
||||
"{foo..}".format(foo=1)
|
||||
"{foo..bar}".format(foo=1)
|
||||
|
||||
# The following are all "good" uses of .format
|
||||
"{.__class__}".format("")
|
||||
"{foo[bar]}".format(foo={"bar": "barv"})
|
||||
"{[bar]}".format({"bar": "barv"})
|
||||
"{:{}} {}".format(1, 15, 2)
|
||||
"{:2}".format(1)
|
||||
"{foo}-{}".format(1, foo=2)
|
||||
a = ()
|
||||
"{}".format(*a)
|
||||
k = {}
|
||||
"{foo}".format(**k)
|
||||
4
resources/test/fixtures/F522.py
vendored
Normal file
4
resources/test/fixtures/F522.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
"{}".format(1, bar=2) # F522
|
||||
"{bar}{}".format(1, bar=2, spam=3) # F522
|
||||
"{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||
12
resources/test/fixtures/F523.py
vendored
Normal file
12
resources/test/fixtures/F523.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# With indexes
|
||||
"{0}".format(1, 2) # F523
|
||||
"{1}".format(1, 2, 3) # F523
|
||||
"{1:{0}}".format(1, 2) # No issues
|
||||
"{1:{0}}".format(1, 2, 3) # F523
|
||||
"{0}{2}".format(1, 2) # F523, # F524
|
||||
|
||||
# With no indexes
|
||||
"{}".format(1, 2) # F523
|
||||
"{}".format(1, 2, 3) # F523
|
||||
"{:{}}".format(1, 2) # No issues
|
||||
"{:{}}".format(1, 2, 3) # F523
|
||||
6
resources/test/fixtures/F524.py
vendored
Normal file
6
resources/test/fixtures/F524.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"{} {}".format(1) # F524
|
||||
"{2}".format() # F524
|
||||
"{bar}".format() # F524
|
||||
"{0} {bar}".format(1) # F524
|
||||
"{0} {bar}".format() # F524
|
||||
"{bar} {0}".format() # F524
|
||||
2
resources/test/fixtures/F525.py
vendored
Normal file
2
resources/test/fixtures/F525.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"{} {1}".format(1, 2) # F525
|
||||
"{0} {}".format(1, 2) # F523, F525
|
||||
10
resources/test/fixtures/F821_3.py
vendored
10
resources/test/fixtures/F821_3.py
vendored
@@ -12,3 +12,13 @@ x: dict["key", "value"]
|
||||
|
||||
# OK
|
||||
x: dict[str, str]
|
||||
|
||||
# OK
|
||||
def unimportant(name):
|
||||
pass
|
||||
|
||||
|
||||
def dang(dict, set, list):
|
||||
unimportant(name=dict["name"])
|
||||
unimportant(name=set["name"])
|
||||
unimportant(name=list["name"])
|
||||
|
||||
30
resources/test/fixtures/F841.py
vendored
30
resources/test/fixtures/F841.py
vendored
@@ -35,3 +35,33 @@ def f4():
|
||||
_ = 1
|
||||
__ = 1
|
||||
_discarded = 1
|
||||
|
||||
|
||||
a = 1
|
||||
|
||||
|
||||
def f5():
|
||||
global a
|
||||
|
||||
# Used in `f7` via `nonlocal`.
|
||||
b = 1
|
||||
|
||||
def f6():
|
||||
# F841
|
||||
b = 1
|
||||
|
||||
def f7():
|
||||
nonlocal b
|
||||
|
||||
|
||||
def f6():
|
||||
annotations = []
|
||||
assert len([annotations for annotations in annotations])
|
||||
|
||||
|
||||
def f7():
|
||||
def connect():
|
||||
return None, None
|
||||
|
||||
with connect() as (connection, cursor):
|
||||
cursor.execute("SELECT * FROM users")
|
||||
|
||||
15
resources/test/fixtures/FBT.py
vendored
15
resources/test/fixtures/FBT.py
vendored
@@ -38,5 +38,20 @@ def function(
|
||||
def used(do):
|
||||
return do
|
||||
|
||||
|
||||
used("a", True)
|
||||
used(do=True)
|
||||
|
||||
|
||||
# Avoid FBT003 for explicitly allowed methods.
|
||||
"""
|
||||
FBT003 Boolean positional value on dict
|
||||
"""
|
||||
a = {"a": "b"}
|
||||
a.get("hello", False)
|
||||
{}.get("hello", False)
|
||||
{}.setdefault("hello", True)
|
||||
{}.pop("hello", False)
|
||||
{}.pop(True, False)
|
||||
dict.fromkeys(("world",), True)
|
||||
{}.deploy(True, False)
|
||||
|
||||
8
resources/test/fixtures/M001.py
vendored
8
resources/test/fixtures/M001.py
vendored
@@ -18,6 +18,14 @@ def f() -> None:
|
||||
# Invalid (and unimplemented)
|
||||
d = 1 # noqa: F841, W191
|
||||
|
||||
# fmt: off
|
||||
# Invalid - no space before #
|
||||
d = 1# noqa: E501
|
||||
|
||||
# Invalid - many spaces before #
|
||||
d = 1 # noqa: E501
|
||||
# fmt: on
|
||||
|
||||
|
||||
# Valid
|
||||
_ = """Lorem ipsum dolor sit amet.
|
||||
|
||||
8
resources/test/fixtures/N804.py
vendored
8
resources/test/fixtures/N804.py
vendored
@@ -30,6 +30,14 @@ class Class:
|
||||
def __init_subclass__(self, default_name, **kwargs):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def class_method_with_positional_only_argument(cls, x, /, other):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def bad_class_method_with_positional_only_argument(self, x, /, other):
|
||||
...
|
||||
|
||||
|
||||
class MetaClass(ABCMeta):
|
||||
def bad_method(self):
|
||||
|
||||
21
resources/test/fixtures/N807.py
vendored
21
resources/test/fixtures/N807.py
vendored
@@ -10,6 +10,27 @@ def good__():
|
||||
pass
|
||||
|
||||
|
||||
def nested():
|
||||
def __bad__():
|
||||
pass
|
||||
|
||||
def __good():
|
||||
pass
|
||||
|
||||
def good__():
|
||||
pass
|
||||
|
||||
|
||||
class Class:
|
||||
def __good__(self):
|
||||
pass
|
||||
|
||||
|
||||
# https://peps.python.org/pep-0562/
|
||||
def __getattr__(name):
|
||||
pass
|
||||
|
||||
|
||||
# https://peps.python.org/pep-0562/
|
||||
def __dir__():
|
||||
pass
|
||||
|
||||
5
resources/test/fixtures/RUF101_0.py
vendored
Normal file
5
resources/test/fixtures/RUF101_0.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
exit(2)
|
||||
10
resources/test/fixtures/RUF101_1.py
vendored
Normal file
10
resources/test/fixtures/RUF101_1.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import sys
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
exit(1)
|
||||
|
||||
|
||||
sys.exit(2)
|
||||
7
resources/test/fixtures/RUF101_2.py
vendored
Normal file
7
resources/test/fixtures/RUF101_2.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import sys as sys2
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
exit(1)
|
||||
7
resources/test/fixtures/RUF101_3.py
vendored
Normal file
7
resources/test/fixtures/RUF101_3.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from sys import exit
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
exit(1)
|
||||
7
resources/test/fixtures/RUF101_4.py
vendored
Normal file
7
resources/test/fixtures/RUF101_4.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from sys import exit as exit2
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
exit(1)
|
||||
7
resources/test/fixtures/RUF101_5.py
vendored
Normal file
7
resources/test/fixtures/RUF101_5.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from sys import *
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
exit(1)
|
||||
12
resources/test/fixtures/RUF101_6.py
vendored
Normal file
12
resources/test/fixtures/RUF101_6.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
exit(0)
|
||||
|
||||
|
||||
def exit(e):
|
||||
pass
|
||||
|
||||
|
||||
exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
exit(2)
|
||||
14
resources/test/fixtures/T100.py
vendored
Normal file
14
resources/test/fixtures/T100.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
breakpoint()
|
||||
|
||||
|
||||
import pdb
|
||||
from builtins import breakpoint
|
||||
from pdb import set_trace as st
|
||||
from celery.contrib.rdb import set_trace
|
||||
from celery.contrib import rdb
|
||||
import celery.contrib.rdb
|
||||
|
||||
|
||||
breakpoint()
|
||||
st()
|
||||
set_trace()
|
||||
4
resources/test/fixtures/U006.py
vendored
4
resources/test/fixtures/U006.py
vendored
@@ -24,3 +24,7 @@ from typing import List as IList
|
||||
|
||||
def f(x: IList[str]) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: "List[str]") -> None:
|
||||
...
|
||||
|
||||
29
resources/test/fixtures/U007.py
vendored
29
resources/test/fixtures/U007.py
vendored
@@ -1,40 +1,43 @@
|
||||
import typing
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
|
||||
def f(x: Optional[str]) -> None:
|
||||
...
|
||||
|
||||
|
||||
import typing
|
||||
|
||||
|
||||
def f(x: typing.Optional[str]) -> None:
|
||||
...
|
||||
|
||||
|
||||
from typing import Union
|
||||
|
||||
|
||||
def f(x: Union[str, int, Union[float, bytes]]) -> None:
|
||||
...
|
||||
|
||||
|
||||
import typing
|
||||
|
||||
|
||||
def f(x: typing.Union[str, int]) -> None:
|
||||
...
|
||||
|
||||
|
||||
from typing import Union
|
||||
def f(x: typing.Union[(str, int)]) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: typing.Union[(str, int), float]) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: "Union[str, int, Union[float, bytes]]") -> None:
|
||||
...
|
||||
|
||||
|
||||
import typing
|
||||
|
||||
|
||||
def f(x: "typing.Union[str, int]") -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: Union["str", int]) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: Union[("str", "int"), float]) -> None:
|
||||
...
|
||||
|
||||
2
resources/test/fixtures/U009_0.py
vendored
2
resources/test/fixtures/U009_0.py
vendored
@@ -1,3 +1,3 @@
|
||||
# coding=utf8
|
||||
|
||||
print('Hello world')
|
||||
print("Hello world")
|
||||
|
||||
3
resources/test/fixtures/U009_4.py
vendored
Normal file
3
resources/test/fixtures/U009_4.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# coding=utf8 # noqa: U009
|
||||
|
||||
print("Hello world")
|
||||
4
resources/test/fixtures/U013.py
vendored
4
resources/test/fixtures/U013.py
vendored
@@ -1,4 +1,5 @@
|
||||
from typing import TypedDict, NotRequired, Literal
|
||||
import typing
|
||||
|
||||
# dict literal
|
||||
MyType1 = TypedDict("MyType1", {"a": int, "b": str})
|
||||
@@ -27,3 +28,6 @@ MyType9 = TypedDict("MyType9", {"in": int, "x-y": int})
|
||||
|
||||
# using Literal type
|
||||
MyType10 = TypedDict("MyType10", {"key": Literal["value"]})
|
||||
|
||||
# using namespace TypedDict
|
||||
MyType11 = typing.TypedDict("MyType11", {"key": int})
|
||||
|
||||
22
resources/test/fixtures/U014.py
vendored
Normal file
22
resources/test/fixtures/U014.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
from typing import NamedTuple
|
||||
import typing
|
||||
|
||||
# with complex annotations
|
||||
NT1 = NamedTuple("NT1", [("a", int), ("b", tuple[str, ...])])
|
||||
|
||||
# with default values as list
|
||||
NT2 = NamedTuple(
|
||||
"NT2",
|
||||
[("a", int), ("b", str), ("c", list[bool])],
|
||||
defaults=["foo", [True]],
|
||||
)
|
||||
|
||||
# with namespace
|
||||
NT3 = typing.NamedTuple("NT3", [("a", int), ("b", str)])
|
||||
|
||||
# with too many default values
|
||||
NT4 = NamedTuple(
|
||||
"NT4",
|
||||
[("a", int), ("b", str)],
|
||||
defaults=[1, "bar", "baz"],
|
||||
)
|
||||
80
resources/test/fixtures/U015.py
vendored
Normal file
80
resources/test/fixtures/U015.py
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
open("foo", "U")
|
||||
open("foo", "Ur")
|
||||
open("foo", "Ub")
|
||||
open("foo", "rUb")
|
||||
open("foo", "r")
|
||||
open("foo", "rt")
|
||||
open("f", "r", encoding="UTF-8")
|
||||
open("f", "wt")
|
||||
|
||||
with open("foo", "U") as f:
|
||||
pass
|
||||
with open("foo", "Ur") as f:
|
||||
pass
|
||||
with open("foo", "Ub") as f:
|
||||
pass
|
||||
with open("foo", "rUb") as f:
|
||||
pass
|
||||
with open("foo", "r") as f:
|
||||
pass
|
||||
with open("foo", "rt") as f:
|
||||
pass
|
||||
with open("foo", "r", encoding="UTF-8") as f:
|
||||
pass
|
||||
with open("foo", "wt") as f:
|
||||
pass
|
||||
|
||||
open(f("a", "b", "c"), "U")
|
||||
open(f("a", "b", "c"), "Ub")
|
||||
|
||||
with open(f("a", "b", "c"), "U") as f:
|
||||
pass
|
||||
with open(f("a", "b", "c"), "Ub") as f:
|
||||
pass
|
||||
|
||||
with open("foo", "U") as fa, open("bar", "U") as fb:
|
||||
pass
|
||||
with open("foo", "Ub") as fa, open("bar", "Ub") as fb:
|
||||
pass
|
||||
|
||||
open("foo", mode="U")
|
||||
open(name="foo", mode="U")
|
||||
open(mode="U", name="foo")
|
||||
|
||||
with open("foo", mode="U") as f:
|
||||
pass
|
||||
with open(name="foo", mode="U") as f:
|
||||
pass
|
||||
with open(mode="U", name="foo") as f:
|
||||
pass
|
||||
|
||||
open("foo", mode="Ub")
|
||||
open(name="foo", mode="Ub")
|
||||
open(mode="Ub", name="foo")
|
||||
|
||||
with open("foo", mode="Ub") as f:
|
||||
pass
|
||||
with open(name="foo", mode="Ub") as f:
|
||||
pass
|
||||
with open(mode="Ub", name="foo") as f:
|
||||
pass
|
||||
|
||||
open(file="foo", mode='U', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='U')
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='U', newline=None, closefd=True, opener=None)
|
||||
open(mode='U', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
|
||||
open(file="foo", mode='Ub', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='Ub')
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='Ub', newline=None, closefd=True, opener=None)
|
||||
open(mode='Ub', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
|
||||
open = 1
|
||||
open("foo", "U")
|
||||
open("foo", "Ur")
|
||||
open("foo", "Ub")
|
||||
open("foo", "rUb")
|
||||
open("foo", "r")
|
||||
open("foo", "rt")
|
||||
open("f", "r", encoding="UTF-8")
|
||||
open("f", "wt")
|
||||
@@ -44,7 +44,7 @@ expectation.expected.add((
|
||||
@expect("D407: Missing dashed underline after section ('Returns')",
|
||||
arg_count=3)
|
||||
@expect("D413: Missing blank line after last section ('Raises')", arg_count=3)
|
||||
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
|
||||
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None, **kwargs):
|
||||
"""Fetches rows from a Bigtable.
|
||||
|
||||
Retrieves rows pertaining to the given keys from the Table instance
|
||||
@@ -57,6 +57,7 @@ def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
|
||||
to fetch.
|
||||
other_silly_variable: Another optional variable, that has a much
|
||||
longer name than the other args, and which does nothing.
|
||||
**kwargs: More keyword arguments.
|
||||
|
||||
Returns:
|
||||
A dict mapping keys to the corresponding table row data
|
||||
|
||||
@@ -73,7 +73,7 @@ expectation.expected.add((
|
||||
"(found 'A')", arg_count=3)
|
||||
@expect("D413: Missing blank line after last section ('Examples')",
|
||||
arg_count=3)
|
||||
def foo(var1, var2, long_var_name='hi'):
|
||||
def foo(var1, var2, long_var_name='hi', **kwargs):
|
||||
r"""A one-line summary that does not use variable names.
|
||||
|
||||
Several sentences providing an extended description. Refer to
|
||||
@@ -91,6 +91,8 @@ def foo(var1, var2, long_var_name='hi'):
|
||||
detail, e.g. ``(N,) ndarray`` or ``array_like``.
|
||||
long_var_name : {'hi', 'ho'}, optional
|
||||
Choices in brackets, default first when optional.
|
||||
**kwargs : int
|
||||
More keyword arguments.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
10
resources/test/fixtures/future_annotations.py
vendored
10
resources/test/fixtures/future_annotations.py
vendored
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional
|
||||
|
||||
from models import (
|
||||
Fruit,
|
||||
@@ -28,3 +29,12 @@ class Foo:
|
||||
@classmethod
|
||||
def d(cls) -> Fruit:
|
||||
return cls(x=0, y=0)
|
||||
|
||||
|
||||
def f(x: int) -> List[int]:
|
||||
y = List[int]()
|
||||
y.append(x)
|
||||
return y
|
||||
|
||||
|
||||
x: Optional[int] = None
|
||||
|
||||
4
resources/test/fixtures/isort/order_relative_imports_by_level.py
vendored
Normal file
4
resources/test/fixtures/isort/order_relative_imports_by_level.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
from .a import a
|
||||
from ..a import a
|
||||
from ..b import a
|
||||
from .b import a
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.128"
|
||||
version = "0.0.145"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -8,10 +8,10 @@ anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
codegen = { version = "0.2.0" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
ruff = { path = ".." }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
|
||||
@@ -24,7 +24,7 @@ pub struct Cli {
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
// Build up a map from prefix to matching CheckCodes.
|
||||
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = Default::default();
|
||||
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
|
||||
for check_code in CheckCode::iter() {
|
||||
let as_ref: String = check_code.as_ref().to_string();
|
||||
let prefix_len = as_ref
|
||||
@@ -77,6 +77,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.arg_ref_self()
|
||||
.ret(Type::new("Vec<CheckCode>"))
|
||||
.vis("pub")
|
||||
.line("#[allow(clippy::match_same_arms)]")
|
||||
.line("match self {");
|
||||
for (prefix, codes) in &prefix_to_codes {
|
||||
gen = gen.line(format!(
|
||||
@@ -96,6 +97,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.arg_ref_self()
|
||||
.ret(Type::new("PrefixSpecificity"))
|
||||
.vis("pub")
|
||||
.line("#[allow(clippy::match_same_arms)]")
|
||||
.line("match self {");
|
||||
for prefix in prefix_to_codes.keys() {
|
||||
let num_numeric = prefix.chars().filter(|char| char.is_numeric()).count();
|
||||
@@ -104,7 +106,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
2 => "Tens",
|
||||
1 => "Hundreds",
|
||||
0 => "Category",
|
||||
_ => panic!("Invalid prefix: {}", prefix),
|
||||
_ => panic!("Invalid prefix: {prefix}"),
|
||||
};
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => PrefixSpecificity::{},",
|
||||
@@ -115,7 +117,8 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
|
||||
// Construct the output contents.
|
||||
let mut output = String::new();
|
||||
output.push_str("//! File automatically generated by examples/generate_check_code_prefix.rs.");
|
||||
output
|
||||
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
output.push_str("use serde::{{Serialize, Deserialize}};");
|
||||
@@ -127,13 +130,28 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
output.push_str(&scope.to_string());
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
// Add the list of output categories (not generated).
|
||||
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
|
||||
output.push('\n');
|
||||
for prefix in prefix_to_codes.keys() {
|
||||
if prefix.chars().all(char::is_alphabetic) {
|
||||
output.push_str(&format!("CheckCodePrefix::{prefix},"));
|
||||
output.push('\n');
|
||||
}
|
||||
}
|
||||
output.push_str("];");
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
// Write the output to `src/checks_gen.rs` (or stdout).
|
||||
if cli.dry_run {
|
||||
println!("{}", output);
|
||||
println!("{output}");
|
||||
} else {
|
||||
let mut f = OpenOptions::new().write(true).truncate(true).open(FILE)?;
|
||||
write!(f, "{}", output)?;
|
||||
write!(f, "{output}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -61,7 +61,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
}
|
||||
|
||||
if cli.dry_run {
|
||||
print!("{}", output);
|
||||
print!("{output}");
|
||||
} else {
|
||||
// Read the existing file.
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
@@ -84,9 +84,9 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
|
||||
// Write the prefix, new contents, and suffix.
|
||||
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
|
||||
write!(f, "{}\n\n", prefix)?;
|
||||
write!(f, "{}", output)?;
|
||||
write!(f, "{}", suffix)?;
|
||||
write!(f, "{prefix}\n\n")?;
|
||||
write!(f, "{output}")?;
|
||||
write!(f, "{suffix}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
|
||||
let mut generator = SourceGenerator::new();
|
||||
generator.unparse_suite(&python_ast)?;
|
||||
generator.unparse_suite(&python_ast);
|
||||
println!("{}", generator.generate()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
pub mod generate_check_code_prefix;
|
||||
pub mod generate_rules_table;
|
||||
pub mod generate_source_code;
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
#![allow(
|
||||
clippy::collapsible_else_if,
|
||||
clippy::collapsible_if,
|
||||
clippy::implicit_hasher,
|
||||
clippy::match_same_arms,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::must_use_candidate,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use ruff_dev::{
|
||||
|
||||
@@ -17,6 +17,6 @@ pub struct Cli {
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
|
||||
println!("{:#?}", python_ast);
|
||||
println!("{python_ast:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Print the LibCST CST for a given Python file.
|
||||
//! Print the `LibCST` CST for a given Python file.
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
@@ -17,7 +17,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
match libcst_native::parse_module(&contents, None) {
|
||||
Ok(python_cst) => {
|
||||
println!("{:#?}", python_cst);
|
||||
println!("{python_cst:#?}");
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(anyhow::anyhow!("Failed to parse CST")),
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct Cli {
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
let contents = fs::read_to_string(&cli.file)?;
|
||||
for (_, tok, _) in lexer::make_tokenizer(&contents).flatten() {
|
||||
println!("{:#?}", tok);
|
||||
println!("{tok:#?}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, StmtKind};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{
|
||||
Arguments, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind,
|
||||
};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::SourceCodeLocator;
|
||||
|
||||
#[inline(always)]
|
||||
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
match &expr.node {
|
||||
ExprKind::Call { func, .. } => {
|
||||
@@ -21,7 +25,6 @@ fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
}
|
||||
|
||||
/// Convert an `Expr` to its call path (like `List`, or `typing.List`).
|
||||
#[inline(always)]
|
||||
pub fn compose_call_path(expr: &Expr) -> Option<String> {
|
||||
let segments = collect_call_paths(expr);
|
||||
if segments.is_empty() {
|
||||
@@ -32,7 +35,6 @@ pub fn compose_call_path(expr: &Expr) -> Option<String> {
|
||||
}
|
||||
|
||||
/// Convert an `Expr` to its call path segments (like ["typing", "List"]).
|
||||
#[inline(always)]
|
||||
pub fn collect_call_paths(expr: &Expr) -> Vec<&str> {
|
||||
let mut segments = vec![];
|
||||
collect_call_path_inner(expr, &mut segments);
|
||||
@@ -42,7 +44,7 @@ pub fn collect_call_paths(expr: &Expr) -> Vec<&str> {
|
||||
/// Rewrite any import aliases on a call path.
|
||||
pub fn dealias_call_path<'a>(
|
||||
call_path: Vec<&'a str>,
|
||||
import_aliases: &FnvHashMap<&str, &'a str>,
|
||||
import_aliases: &FxHashMap<&str, &'a str>,
|
||||
) -> Vec<&'a str> {
|
||||
if let Some(head) = call_path.first() {
|
||||
if let Some(origin) = import_aliases.get(head) {
|
||||
@@ -76,8 +78,8 @@ pub fn match_module_member(
|
||||
expr: &Expr,
|
||||
module: &str,
|
||||
member: &str,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
import_aliases: &FnvHashMap<&str, &str>,
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
import_aliases: &FxHashMap<&str, &str>,
|
||||
) -> bool {
|
||||
match_call_path(
|
||||
&dealias_call_path(collect_call_paths(expr), import_aliases),
|
||||
@@ -94,7 +96,7 @@ pub fn match_call_path(
|
||||
call_path: &[&str],
|
||||
module: &str,
|
||||
member: &str,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
) -> bool {
|
||||
// If we have no segments, we can't ever match.
|
||||
let num_segments = call_path.len();
|
||||
@@ -118,10 +120,9 @@ pub fn match_call_path(
|
||||
// `Match`).
|
||||
if num_segments == 0 {
|
||||
module.is_empty()
|
||||
|| from_imports
|
||||
.get(module)
|
||||
.map(|imports| imports.contains(member) || imports.contains("*"))
|
||||
.unwrap_or(false)
|
||||
|| from_imports.get(module).map_or(false, |imports| {
|
||||
imports.contains(member) || imports.contains("*")
|
||||
})
|
||||
} else {
|
||||
let components: Vec<&str> = module.split('.').collect();
|
||||
|
||||
@@ -145,8 +146,7 @@ pub fn match_call_path(
|
||||
let member = components[cut];
|
||||
if from_imports
|
||||
.get(&module.as_str())
|
||||
.map(|imports| imports.contains(member))
|
||||
.unwrap_or(false)
|
||||
.map_or(false, |imports| imports.contains(member))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -215,6 +215,27 @@ pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<Vec<&str>> {
|
||||
handler_names
|
||||
}
|
||||
|
||||
/// Return the set of all bound argument names.
|
||||
pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
|
||||
let mut arg_names: FxHashSet<&'a str> = FxHashSet::default();
|
||||
for arg in &arguments.posonlyargs {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
for arg in &arguments.args {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
if let Some(arg) = &arguments.vararg {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
for arg in &arguments.kwonlyargs {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
if let Some(arg) = &arguments.kwarg {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
arg_names
|
||||
}
|
||||
|
||||
/// Returns `true` if a call is an argumented `super` invocation.
|
||||
pub fn is_super_call_with_arguments(func: &Expr, args: &[Expr]) -> bool {
|
||||
// Check: is this a `super` call?
|
||||
@@ -250,7 +271,7 @@ pub fn to_module_and_member(target: &str) -> (&str, &str) {
|
||||
|
||||
/// Convert a location within a file (relative to `base`) to an absolute
|
||||
/// position.
|
||||
pub fn to_absolute(relative: &Location, base: &Location) -> Location {
|
||||
pub fn to_absolute(relative: Location, base: Location) -> Location {
|
||||
if relative.row() == 1 {
|
||||
Location::new(
|
||||
relative.row() + base.row() - 1,
|
||||
@@ -261,10 +282,38 @@ pub fn to_absolute(relative: &Location, base: &Location) -> Location {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` has leading content.
|
||||
pub fn match_leading_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
let range = Range {
|
||||
location: Location::new(stmt.location.row(), 0),
|
||||
end_location: stmt.location,
|
||||
};
|
||||
let prefix = locator.slice_source_code_range(&range);
|
||||
prefix.chars().any(|char| !char.is_whitespace())
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` has trailing content.
|
||||
pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
let range = Range {
|
||||
location: stmt.end_location.unwrap(),
|
||||
end_location: Location::new(stmt.end_location.unwrap().row() + 1, 0),
|
||||
};
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
for char in suffix.chars() {
|
||||
if char == '#' {
|
||||
return false;
|
||||
}
|
||||
if !char.is_whitespace() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_parser::parser;
|
||||
|
||||
use crate::ast::helpers::match_module_member;
|
||||
@@ -276,8 +325,8 @@ mod tests {
|
||||
&expr,
|
||||
"",
|
||||
"list",
|
||||
&FnvHashMap::default(),
|
||||
&FnvHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -289,8 +338,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::default(),
|
||||
&FnvHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -302,16 +351,16 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::default(),
|
||||
&FnvHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
));
|
||||
let expr = parser::parse_expression("re.Match", "<filename>")?;
|
||||
assert!(!match_module_member(
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::default(),
|
||||
&FnvHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
&FxHashMap::default(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -323,8 +372,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["*"]))]),
|
||||
&FnvHashMap::default()
|
||||
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["*"]))]),
|
||||
&FxHashMap::default()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -336,8 +385,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))]),
|
||||
&FnvHashMap::default()
|
||||
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["Match"]))]),
|
||||
&FxHashMap::default()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -349,8 +398,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))]),
|
||||
&FnvHashMap::default()
|
||||
&FxHashMap::from_iter([("typing", FxHashSet::from_iter(["re"]))]),
|
||||
&FxHashMap::default()
|
||||
));
|
||||
|
||||
let expr = parser::parse_expression("match.Match", "<filename>")?;
|
||||
@@ -358,8 +407,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re.match",
|
||||
"Match",
|
||||
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["match"]))]),
|
||||
&FnvHashMap::default()
|
||||
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["match"]))]),
|
||||
&FxHashMap::default()
|
||||
));
|
||||
|
||||
let expr = parser::parse_expression("re.match.Match", "<filename>")?;
|
||||
@@ -367,8 +416,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re.match",
|
||||
"Match",
|
||||
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))]),
|
||||
&FnvHashMap::default()
|
||||
&FxHashMap::from_iter([("typing", FxHashSet::from_iter(["re"]))]),
|
||||
&FxHashMap::default()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -380,8 +429,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))]),
|
||||
&FnvHashMap::from_iter([("IMatch", "Match")]),
|
||||
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["Match"]))]),
|
||||
&FxHashMap::from_iter([("IMatch", "Match")]),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -393,8 +442,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::default(),
|
||||
&FnvHashMap::from_iter([("t", "typing.re")]),
|
||||
&FxHashMap::default(),
|
||||
&FxHashMap::from_iter([("t", "typing.re")]),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -406,8 +455,8 @@ mod tests {
|
||||
&expr,
|
||||
"typing.re",
|
||||
"Match",
|
||||
&FnvHashMap::default(),
|
||||
&FnvHashMap::from_iter([("t", "typing")]),
|
||||
&FxHashMap::default(),
|
||||
&FxHashMap::from_iter([("t", "typing")]),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod operations;
|
||||
pub mod relocate;
|
||||
pub mod types;
|
||||
pub mod visitor;
|
||||
pub mod whitespace;
|
||||
|
||||
@@ -4,8 +4,6 @@ use crate::ast::types::{BindingKind, Scope};
|
||||
|
||||
/// Extract the names bound to a given __all__ assignment.
|
||||
pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
let mut names: Vec<String> = vec![];
|
||||
|
||||
fn add_to_names(names: &mut Vec<String>, elts: &[Expr]) {
|
||||
for elt in elts {
|
||||
if let ExprKind::Constant {
|
||||
@@ -13,11 +11,13 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
..
|
||||
} = &elt.node
|
||||
{
|
||||
names.push(value.to_string())
|
||||
names.push(value.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut names: Vec<String> = vec![];
|
||||
|
||||
// Grab the existing bound __all__ values.
|
||||
if let StmtKind::AugAssign { .. } = &stmt.node {
|
||||
if let Some(binding) = scope.values.get("__all__") {
|
||||
@@ -35,7 +35,7 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
} {
|
||||
match &value.node {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
add_to_names(&mut names, elts)
|
||||
add_to_names(&mut names, elts);
|
||||
}
|
||||
ExprKind::BinOp { left, right, .. } => {
|
||||
let mut current_left = left;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use rustpython_ast::{Expr, Keyword};
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Expr, Keyword, Stmt};
|
||||
use rustpython_parser::ast::{Located, Location};
|
||||
|
||||
fn id() -> usize {
|
||||
@@ -9,6 +9,12 @@ fn id() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Node<'a> {
|
||||
Stmt(&'a Stmt),
|
||||
Expr(&'a Expr),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Range {
|
||||
pub location: Location,
|
||||
@@ -19,9 +25,7 @@ impl Range {
|
||||
pub fn from_located<T>(located: &Located<T>) -> Self {
|
||||
Range {
|
||||
location: located.location,
|
||||
end_location: located
|
||||
.end_location
|
||||
.expect("AST nodes should have end_location."),
|
||||
end_location: located.end_location.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +58,7 @@ pub struct Scope<'a> {
|
||||
pub id: usize,
|
||||
pub kind: ScopeKind<'a>,
|
||||
pub import_starred: bool,
|
||||
pub values: FnvHashMap<&'a str, Binding>,
|
||||
pub values: FxHashMap<&'a str, Binding>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
@@ -63,7 +67,7 @@ impl<'a> Scope<'a> {
|
||||
id: id(),
|
||||
kind,
|
||||
import_starred: false,
|
||||
values: FnvHashMap::default(),
|
||||
values: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +87,7 @@ pub enum BindingKind {
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
Definition,
|
||||
|
||||
@@ -19,8 +19,8 @@ pub trait Visitor<'a> {
|
||||
fn visit_constant(&mut self, constant: &'a Constant) {
|
||||
walk_constant(self, constant);
|
||||
}
|
||||
fn visit_expr_context(&mut self, expr_content: &'a ExprContext) {
|
||||
walk_expr_context(self, expr_content);
|
||||
fn visit_expr_context(&mut self, expr_context: &'a ExprContext) {
|
||||
walk_expr_context(self, expr_context);
|
||||
}
|
||||
fn visit_boolop(&mut self, boolop: &'a Boolop) {
|
||||
walk_boolop(self, boolop);
|
||||
@@ -249,7 +249,7 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
visitor.visit_stmt(stmt);
|
||||
}
|
||||
for excepthandler in handlers {
|
||||
visitor.visit_excepthandler(excepthandler)
|
||||
visitor.visit_excepthandler(excepthandler);
|
||||
}
|
||||
for stmt in orelse {
|
||||
visitor.visit_stmt(stmt);
|
||||
@@ -447,7 +447,7 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
|
||||
pub fn walk_constant<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, constant: &'a Constant) {
|
||||
if let Constant::Tuple(constants) = constant {
|
||||
for constant in constants {
|
||||
visitor.visit_constant(constant)
|
||||
visitor.visit_constant(constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -456,8 +456,8 @@ pub fn walk_comprehension<'a, V: Visitor<'a> + ?Sized>(
|
||||
visitor: &mut V,
|
||||
comprehension: &'a Comprehension,
|
||||
) {
|
||||
visitor.visit_expr(&comprehension.target);
|
||||
visitor.visit_expr(&comprehension.iter);
|
||||
visitor.visit_expr(&comprehension.target);
|
||||
for expr in &comprehension.ifs {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
@@ -577,7 +577,6 @@ pub fn walk_pattern<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, pattern: &'a P
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline(always)]
|
||||
pub fn walk_expr_context<'a, V: Visitor<'a> + ?Sized>(
|
||||
visitor: &mut V,
|
||||
expr_context: &'a ExprContext,
|
||||
@@ -585,21 +584,16 @@ pub fn walk_expr_context<'a, V: Visitor<'a> + ?Sized>(
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline(always)]
|
||||
pub fn walk_boolop<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, boolop: &'a Boolop) {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline(always)]
|
||||
pub fn walk_operator<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, operator: &'a Operator) {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline(always)]
|
||||
pub fn walk_unaryop<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, unaryop: &'a Unaryop) {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline(always)]
|
||||
pub fn walk_cmpop<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, cmpop: &'a Cmpop) {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[inline(always)]
|
||||
pub fn walk_alias<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, alias: &'a Alias) {}
|
||||
|
||||
@@ -3,12 +3,6 @@ use rustpython_ast::{Located, Location};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
|
||||
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
|
||||
];
|
||||
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
|
||||
|
||||
/// Extract the leading words from a line of text.
|
||||
pub fn leading_words(line: &str) -> String {
|
||||
line.trim()
|
||||
@@ -25,7 +19,7 @@ pub fn leading_space(line: &str) -> String {
|
||||
}
|
||||
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<'a, T>(checker: &'a Checker, located: &Located<T>) -> String {
|
||||
pub fn indentation<T>(checker: &Checker, located: &Located<T>) -> String {
|
||||
let range = Range::from_located(located);
|
||||
checker
|
||||
.locator
|
||||
@@ -10,8 +10,6 @@ use crate::autofix::{Fix, Patch};
|
||||
use crate::checks::Check;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
// TODO(charlie): The model here is awkward because `Apply` is only relevant at
|
||||
// higher levels in the execution flow.
|
||||
#[derive(Hash)]
|
||||
pub enum Mode {
|
||||
Generate,
|
||||
@@ -19,55 +17,55 @@ pub enum Mode {
|
||||
None,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
/// Return `true` if a patch should be generated under the given `Mode`.
|
||||
pub fn patch(&self) -> bool {
|
||||
match &self {
|
||||
Mode::Generate => true,
|
||||
Mode::Apply => true,
|
||||
Mode::None => false,
|
||||
impl From<bool> for Mode {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Mode::Apply
|
||||
} else {
|
||||
Mode::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Mode {
|
||||
fn from(value: bool) -> Self {
|
||||
impl From<&Mode> for bool {
|
||||
fn from(value: &Mode) -> Self {
|
||||
match value {
|
||||
true => Mode::Apply,
|
||||
false => Mode::None,
|
||||
Mode::Generate | Mode::Apply => true,
|
||||
Mode::None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Auto-fix errors in a file, and write the fixed source code to disk.
|
||||
pub fn fix_file<'a>(
|
||||
checks: &'a mut [Check],
|
||||
checks: &'a [Check],
|
||||
locator: &'a SourceCodeLocator<'a>,
|
||||
) -> Option<Cow<'a, str>> {
|
||||
) -> Option<(Cow<'a, str>, usize)> {
|
||||
if checks.iter().all(|check| check.fix.is_none()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(apply_fixes(
|
||||
checks.iter_mut().filter_map(|check| check.fix.as_mut()),
|
||||
checks.iter().filter_map(|check| check.fix.as_ref()),
|
||||
locator,
|
||||
))
|
||||
}
|
||||
|
||||
/// Apply a series of fixes.
|
||||
fn apply_fixes<'a>(
|
||||
fixes: impl Iterator<Item = &'a mut Fix>,
|
||||
fixes: impl Iterator<Item = &'a Fix>,
|
||||
locator: &'a SourceCodeLocator<'a>,
|
||||
) -> Cow<'a, str> {
|
||||
) -> (Cow<'a, str>, usize) {
|
||||
let mut output = RopeBuilder::new();
|
||||
let mut last_pos: Location = Location::new(1, 0);
|
||||
let mut applied: BTreeSet<&Patch> = BTreeSet::default();
|
||||
let mut num_fixed: usize = 0;
|
||||
|
||||
for fix in fixes.sorted_by_key(|fix| fix.patch.location) {
|
||||
// If we already applied an identical fix as part of another correction, skip
|
||||
// any re-application.
|
||||
if applied.contains(&fix.patch) {
|
||||
fix.applied = true;
|
||||
num_fixed += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -90,19 +88,18 @@ fn apply_fixes<'a>(
|
||||
// Track that the fix was applied.
|
||||
last_pos = fix.patch.end_location;
|
||||
applied.insert(&fix.patch);
|
||||
fix.applied = true;
|
||||
num_fixed += 1;
|
||||
}
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = locator.slice_source_code_at(&last_pos);
|
||||
let slice = locator.slice_source_code_at(last_pos);
|
||||
output.append(&slice);
|
||||
|
||||
Cow::from(output.finish())
|
||||
(Cow::from(output.finish()), num_fixed)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::autofix::fixer::apply_fixes;
|
||||
@@ -110,115 +107,117 @@ mod tests {
|
||||
use crate::SourceCodeLocator;
|
||||
|
||||
#[test]
|
||||
fn empty_file() -> Result<()> {
|
||||
let mut fixes = vec![];
|
||||
let locator = SourceCodeLocator::new("");
|
||||
let actual = apply_fixes(fixes.iter_mut(), &locator);
|
||||
let expected = "";
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
Ok(())
|
||||
fn empty_file() {
|
||||
let fixes = vec![];
|
||||
let locator = SourceCodeLocator::new(r#""#);
|
||||
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
|
||||
assert_eq!(contents, "");
|
||||
assert_eq!(fixed, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_single_replacement() -> Result<()> {
|
||||
let mut fixes = vec![Fix {
|
||||
fn apply_single_replacement() {
|
||||
let fixes = vec![Fix {
|
||||
patch: Patch {
|
||||
content: "Bar".to_string(),
|
||||
location: Location::new(1, 8),
|
||||
end_location: Location::new(1, 14),
|
||||
},
|
||||
applied: false,
|
||||
}];
|
||||
let locator = SourceCodeLocator::new(
|
||||
"class A(object):
|
||||
...
|
||||
",
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let actual = apply_fixes(fixes.iter_mut(), &locator);
|
||||
|
||||
let expected = "class A(Bar):
|
||||
...
|
||||
";
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
Ok(())
|
||||
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
|
||||
assert_eq!(
|
||||
contents,
|
||||
r#"
|
||||
class A(Bar):
|
||||
...
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixed, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_single_removal() -> Result<()> {
|
||||
let mut fixes = vec![Fix {
|
||||
fn apply_single_removal() {
|
||||
let fixes = vec![Fix {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
content: String::new(),
|
||||
location: Location::new(1, 7),
|
||||
end_location: Location::new(1, 15),
|
||||
},
|
||||
applied: false,
|
||||
}];
|
||||
let locator = SourceCodeLocator::new(
|
||||
"class A(object):
|
||||
...
|
||||
",
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let actual = apply_fixes(fixes.iter_mut(), &locator);
|
||||
|
||||
let expected = "class A:
|
||||
...
|
||||
";
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
Ok(())
|
||||
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
|
||||
assert_eq!(
|
||||
contents,
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixed, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_double_removal() -> Result<()> {
|
||||
let mut fixes = vec![
|
||||
fn apply_double_removal() {
|
||||
let fixes = vec![
|
||||
Fix {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
content: String::new(),
|
||||
location: Location::new(1, 7),
|
||||
end_location: Location::new(1, 16),
|
||||
},
|
||||
applied: false,
|
||||
},
|
||||
Fix {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
content: String::new(),
|
||||
location: Location::new(1, 16),
|
||||
end_location: Location::new(1, 23),
|
||||
},
|
||||
applied: false,
|
||||
},
|
||||
];
|
||||
let locator = SourceCodeLocator::new(
|
||||
"class A(object, object):
|
||||
...
|
||||
",
|
||||
r#"
|
||||
class A(object, object):
|
||||
...
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let actual = apply_fixes(fixes.iter_mut(), &locator);
|
||||
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
|
||||
|
||||
let expected = "class A:
|
||||
...
|
||||
";
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
Ok(())
|
||||
assert_eq!(
|
||||
contents,
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixed, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_overlapping_fixes() -> Result<()> {
|
||||
let mut fixes = vec![
|
||||
fn ignore_overlapping_fixes() {
|
||||
let fixes = vec![
|
||||
Fix {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
content: String::new(),
|
||||
location: Location::new(1, 7),
|
||||
end_location: Location::new(1, 15),
|
||||
},
|
||||
applied: false,
|
||||
},
|
||||
Fix {
|
||||
patch: Patch {
|
||||
@@ -226,22 +225,24 @@ mod tests {
|
||||
location: Location::new(1, 9),
|
||||
end_location: Location::new(1, 11),
|
||||
},
|
||||
applied: false,
|
||||
},
|
||||
];
|
||||
let locator = SourceCodeLocator::new(
|
||||
"class A(object):
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
",
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let actual = apply_fixes(fixes.iter_mut(), &locator);
|
||||
|
||||
let expected = "class A:
|
||||
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
|
||||
assert_eq!(
|
||||
contents,
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
";
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
Ok(())
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixed, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,16 @@ pub struct Patch {
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Fix {
|
||||
pub patch: Patch,
|
||||
pub applied: bool,
|
||||
}
|
||||
|
||||
impl Fix {
|
||||
pub fn deletion(start: Location, end: Location) -> Self {
|
||||
Self {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
content: String::new(),
|
||||
location: start,
|
||||
end_location: end,
|
||||
},
|
||||
applied: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +34,6 @@ impl Fix {
|
||||
location: start,
|
||||
end_location: end,
|
||||
},
|
||||
applied: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,18 +44,16 @@ impl Fix {
|
||||
location: at,
|
||||
end_location: at,
|
||||
},
|
||||
applied: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dummy(location: Location) -> Self {
|
||||
Self {
|
||||
patch: Patch {
|
||||
content: "".to_string(),
|
||||
content: String::new(),
|
||||
location,
|
||||
end_location: location,
|
||||
},
|
||||
applied: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
95
src/cache.rs
95
src/cache.rs
@@ -1,19 +1,11 @@
|
||||
// cacache uses asyncd-std which has no wasm support, so currently no caching
|
||||
// support on wasm
|
||||
#![cfg_attr(
|
||||
target_family = "wasm",
|
||||
allow(unused_imports, unused_variables, dead_code)
|
||||
)]
|
||||
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs;
|
||||
use std::fs::{create_dir_all, File, Metadata};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use cacache::Error::EntryNotFound;
|
||||
use filetime::FileTime;
|
||||
use log::error;
|
||||
use path_absolutize::Absolutize;
|
||||
@@ -71,9 +63,10 @@ impl Mode {
|
||||
|
||||
impl From<bool> for Mode {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
true => Mode::ReadWrite,
|
||||
false => Mode::None,
|
||||
if value {
|
||||
Mode::ReadWrite
|
||||
} else {
|
||||
Mode::None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,28 +75,59 @@ fn cache_dir() -> &'static str {
|
||||
"./.ruff_cache"
|
||||
}
|
||||
|
||||
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> String {
|
||||
fn content_dir() -> &'static str {
|
||||
"content"
|
||||
}
|
||||
|
||||
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
CARGO_PKG_VERSION.hash(&mut hasher);
|
||||
path.absolutize().unwrap().hash(&mut hasher);
|
||||
settings.hash(&mut hasher);
|
||||
autofix.hash(&mut hasher);
|
||||
format!(
|
||||
"{}@{}@{}",
|
||||
path.absolutize().unwrap().to_string_lossy(),
|
||||
CARGO_PKG_VERSION,
|
||||
hasher.finish()
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
/// Initialize the cache directory.
|
||||
pub fn init() -> Result<()> {
|
||||
let path = Path::new(cache_dir());
|
||||
|
||||
// Create the cache directories.
|
||||
create_dir_all(path.join(content_dir()))?;
|
||||
|
||||
// Add the CACHEDIR.TAG.
|
||||
if !cachedir::is_tagged(path)? {
|
||||
cachedir::add_tag(path)?;
|
||||
}
|
||||
|
||||
// Add the .gitignore.
|
||||
let gitignore_path = path.join(".gitignore");
|
||||
if !gitignore_path.exists() {
|
||||
let mut file = File::create(gitignore_path)?;
|
||||
file.write_all(b"*")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
|
||||
fs::write(
|
||||
Path::new(cache_dir())
|
||||
.join(content_dir())
|
||||
.join(format!("{key:x}")),
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
let gitignore_path = Path::new(cache_dir()).join(".gitignore");
|
||||
if gitignore_path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
create_dir_all(cache_dir())?;
|
||||
let mut file = File::create(gitignore_path)?;
|
||||
file.write_all(b"*").map_err(|e| e.into())
|
||||
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
|
||||
fs::read(
|
||||
Path::new(cache_dir())
|
||||
.join(content_dir())
|
||||
.join(format!("{key:x}")),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get a value from the cache.
|
||||
pub fn get(
|
||||
path: &Path,
|
||||
metadata: &Metadata,
|
||||
@@ -115,9 +139,8 @@ pub fn get(
|
||||
return None;
|
||||
};
|
||||
|
||||
#[cfg(not(target_family = "wasm"))] // cacache needs async-std which doesn't support wasm
|
||||
match cacache::read_sync(cache_dir(), cache_key(path, settings, autofix)) {
|
||||
Ok(encoded) => match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
if let Ok(encoded) = read_sync(cache_key(path, settings, autofix)) {
|
||||
match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
Ok(CheckResult {
|
||||
metadata: CacheMetadata { mtime },
|
||||
messages,
|
||||
@@ -127,13 +150,12 @@ pub fn get(
|
||||
}
|
||||
}
|
||||
Err(e) => error!("Failed to deserialize encoded cache entry: {e:?}"),
|
||||
},
|
||||
Err(EntryNotFound(..)) => {}
|
||||
Err(e) => error!("Failed to read from cache: {e:?}"),
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Set a value in the cache.
|
||||
pub fn set(
|
||||
path: &Path,
|
||||
metadata: &Metadata,
|
||||
@@ -146,19 +168,16 @@ pub fn set(
|
||||
return;
|
||||
};
|
||||
|
||||
#[cfg(not(target_family = "wasm"))] // modification date not supported on wasm
|
||||
let check_result = CheckResultRef {
|
||||
metadata: &CacheMetadata {
|
||||
mtime: FileTime::from_last_modification_time(metadata).unix_seconds(),
|
||||
},
|
||||
messages,
|
||||
};
|
||||
#[cfg(not(target_family = "wasm"))] // cacache needs async-std which doesn't support wasm
|
||||
if let Err(e) = cacache::write_sync(
|
||||
cache_dir(),
|
||||
if let Err(e) = write_sync(
|
||||
cache_key(path, settings, autofix),
|
||||
bincode::serialize(&check_result).unwrap(),
|
||||
&bincode::serialize(&check_result).unwrap(),
|
||||
) {
|
||||
error!("Failed to write to cache: {e:?}")
|
||||
error!("Failed to write to cache: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
573
src/check_ast.rs
573
src/check_ast.rs
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ use nohash_hasher::IntSet;
|
||||
use rustpython_parser::ast::Suite;
|
||||
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::autofix::fixer;
|
||||
use crate::checks::Check;
|
||||
use crate::isort;
|
||||
use crate::isort::track::ImportTracker;
|
||||
@@ -15,12 +14,12 @@ fn check_import_blocks(
|
||||
tracker: ImportTracker,
|
||||
locator: &SourceCodeLocator,
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
autofix: bool,
|
||||
) -> Vec<Check> {
|
||||
let mut checks = vec![];
|
||||
for block in tracker.into_iter() {
|
||||
if !block.is_empty() {
|
||||
if let Some(check) = isort::plugins::check_imports(block, locator, settings, autofix) {
|
||||
if let Some(check) = isort::plugins::check_imports(&block, locator, settings, autofix) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +32,7 @@ pub fn check_imports(
|
||||
locator: &SourceCodeLocator,
|
||||
exclusions: &IntSet<usize>,
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
autofix: bool,
|
||||
) -> Vec<Check> {
|
||||
let mut tracker = ImportTracker::new(exclusions);
|
||||
for stmt in python_ast {
|
||||
|
||||
@@ -6,7 +6,7 @@ use regex::Regex;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::{fixer, Fix};
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::noqa;
|
||||
use crate::noqa::Directive;
|
||||
@@ -14,15 +14,18 @@ use crate::settings::Settings;
|
||||
|
||||
// Regex from PEP263
|
||||
static CODING_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").expect("Invalid regex"));
|
||||
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap());
|
||||
|
||||
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
|
||||
|
||||
/// Whether the given line is too long and should be reported.
|
||||
fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
|
||||
if length > limit {
|
||||
let mut chunks = line.split_whitespace();
|
||||
if let (Some(first), Some(_)) = (chunks.next(), chunks.next()) {
|
||||
// Do not enforce the line length for commented lines with a single word
|
||||
!(first == "#" && chunks.next().is_none())
|
||||
// Do not enforce the line length for commented lines that end with a URL
|
||||
// or contain only a single word.
|
||||
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
|
||||
} else {
|
||||
// Single word / no printable chars - no way to make the line shorter
|
||||
false
|
||||
@@ -37,7 +40,8 @@ pub fn check_lines(
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
autofix: bool,
|
||||
ignore_noqa: bool,
|
||||
) {
|
||||
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::U009);
|
||||
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
|
||||
@@ -53,6 +57,30 @@ pub fn check_lines(
|
||||
assert!(check.location.row() >= 1);
|
||||
}
|
||||
|
||||
macro_rules! add_if {
|
||||
($check:expr, $noqa:expr) => {{
|
||||
match $noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push($check.kind.code().as_ref());
|
||||
if ignore_noqa {
|
||||
line_checks.push($check);
|
||||
}
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&$check.kind.code().as_ref()) {
|
||||
matches.push($check.kind.code().as_ref());
|
||||
if ignore_noqa {
|
||||
line_checks.push($check);
|
||||
}
|
||||
} else {
|
||||
line_checks.push($check);
|
||||
}
|
||||
}
|
||||
(Directive::None, ..) => line_checks.push($check),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for (lineno, line) in lines.iter().enumerate() {
|
||||
// Grab the noqa (logical) line number for the current (physical) line.
|
||||
@@ -65,21 +93,24 @@ pub fn check_lines(
|
||||
if lineno < 2 {
|
||||
// PEP3120 makes utf-8 the default encoding.
|
||||
if CODING_COMMENT_REGEX.is_match(line) {
|
||||
let line_length = line.len();
|
||||
let mut check = Check::new(
|
||||
CheckKind::PEP3120UnnecessaryCodingComment,
|
||||
Range {
|
||||
location: Location::new(lineno + 1, 0),
|
||||
end_location: Location::new(lineno + 1, line_length + 1),
|
||||
end_location: Location::new(lineno + 2, 0),
|
||||
},
|
||||
);
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(lineno + 1, 0),
|
||||
Location::new(lineno + 1, line_length + 1),
|
||||
Location::new(lineno + 2, 0),
|
||||
));
|
||||
}
|
||||
line_checks.push(check);
|
||||
|
||||
let noqa = noqa_directives.entry(noqa_lineno).or_insert_with(|| {
|
||||
(noqa::extract_noqa_directive(lines[noqa_lineno]), vec![])
|
||||
});
|
||||
add_if!(check, noqa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,15 +132,15 @@ pub fn check_lines(
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
ignored.push(index)
|
||||
ignored.push(index);
|
||||
}
|
||||
(Directive::Codes(_, _, codes), matches) => {
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
ignored.push(index);
|
||||
}
|
||||
}
|
||||
(Directive::None, _) => {}
|
||||
(Directive::None, ..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +148,6 @@ pub fn check_lines(
|
||||
if enforce_line_too_long {
|
||||
let line_length = line.chars().count();
|
||||
if should_enforce_line_length(line, line_length, settings.line_length) {
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
|
||||
let check = Check::new(
|
||||
CheckKind::LineTooLong(line_length, settings.line_length),
|
||||
Range {
|
||||
@@ -129,35 +156,19 @@ pub fn check_lines(
|
||||
},
|
||||
);
|
||||
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
}
|
||||
(Directive::Codes(_, _, codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
} else {
|
||||
line_checks.push(check);
|
||||
}
|
||||
}
|
||||
(Directive::None, _) => line_checks.push(check),
|
||||
}
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
add_if!(check, noqa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce newlines at end of files.
|
||||
// Enforce newlines at end of files (W292).
|
||||
if settings.enabled.contains(&CheckCode::W292) && !contents.ends_with('\n') {
|
||||
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
|
||||
// want to raise W292 anyway).
|
||||
if let Some(line) = lines.last() {
|
||||
let lineno = lines.len() - 1;
|
||||
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
|
||||
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
|
||||
let check = Check::new(
|
||||
CheckKind::NoNewLineAtEndOfFile,
|
||||
Range {
|
||||
@@ -166,27 +177,20 @@ pub fn check_lines(
|
||||
},
|
||||
);
|
||||
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
}
|
||||
(Directive::Codes(_, _, codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
} else {
|
||||
line_checks.push(check);
|
||||
}
|
||||
}
|
||||
(Directive::None, _) => line_checks.push(check),
|
||||
}
|
||||
let lineno = lines.len() - 1;
|
||||
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
add_if!(check, noqa);
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce that the noqa directive was actually used.
|
||||
// Enforce that the noqa directive was actually used (M001).
|
||||
if enforce_noqa {
|
||||
for (row, (directive, matches)) in noqa_directives {
|
||||
match directive {
|
||||
Directive::All(start, end) => {
|
||||
Directive::All(spaces, start, end) => {
|
||||
if matches.is_empty() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnusedNOQA(None),
|
||||
@@ -195,23 +199,23 @@ pub fn check_lines(
|
||||
end_location: Location::new(row + 1, end),
|
||||
},
|
||||
);
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, start - spaces),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
}
|
||||
line_checks.push(check);
|
||||
}
|
||||
}
|
||||
Directive::Codes(start, end, codes) => {
|
||||
Directive::Codes(spaces, start, end, codes) => {
|
||||
let mut invalid_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
for code in codes {
|
||||
if !matches.contains(&code) {
|
||||
invalid_codes.push(code.to_string());
|
||||
} else {
|
||||
if matches.contains(&code) {
|
||||
valid_codes.push(code.to_string());
|
||||
} else {
|
||||
invalid_codes.push(code.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,15 +227,15 @@ pub fn check_lines(
|
||||
end_location: Location::new(row + 1, end),
|
||||
},
|
||||
);
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
if valid_codes.is_empty() {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, start - spaces),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
} else {
|
||||
check.amend(Fix::replacement(
|
||||
format!(" # noqa: {}", valid_codes.join(", ")),
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
@@ -245,9 +249,11 @@ pub fn check_lines(
|
||||
}
|
||||
}
|
||||
|
||||
ignored.sort();
|
||||
for index in ignored.iter().rev() {
|
||||
checks.swap_remove(*index);
|
||||
if !ignore_noqa {
|
||||
ignored.sort_unstable();
|
||||
for index in ignored.iter().rev() {
|
||||
checks.swap_remove(*index);
|
||||
}
|
||||
}
|
||||
checks.extend(line_checks);
|
||||
}
|
||||
@@ -257,14 +263,13 @@ mod tests {
|
||||
use nohash_hasher::IntMap;
|
||||
|
||||
use super::check_lines;
|
||||
use crate::autofix::fixer;
|
||||
use crate::checks::{Check, CheckCode};
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[test]
|
||||
fn e501_non_ascii_char() {
|
||||
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
|
||||
let noqa_line_for: IntMap<usize, usize> = Default::default();
|
||||
let noqa_line_for: IntMap<usize, usize> = IntMap::default();
|
||||
let check_with_max_line_length = |line_length: usize| {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
check_lines(
|
||||
@@ -275,7 +280,8 @@ mod tests {
|
||||
line_length,
|
||||
..Settings::for_rule(CheckCode::E501)
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
checks
|
||||
};
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
|
||||
use rustpython_parser::lexer::{LexResult, Tok};
|
||||
|
||||
use crate::autofix::fixer;
|
||||
use crate::checks::{Check, CheckCode};
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::rules::checks::Context;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::{flake8_quotes, pycodestyle, rules, Settings};
|
||||
use crate::{eradicate, flake8_quotes, pycodestyle, rules, Settings};
|
||||
|
||||
pub fn check_tokens(
|
||||
checks: &mut Vec<Check>,
|
||||
locator: &SourceCodeLocator,
|
||||
tokens: &[LexResult],
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
) {
|
||||
autofix: bool,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
let enforce_ambiguous_unicode_character = settings.enabled.contains(&CheckCode::RUF001)
|
||||
|| settings.enabled.contains(&CheckCode::RUF002)
|
||||
|| settings.enabled.contains(&CheckCode::RUF003);
|
||||
@@ -23,10 +23,11 @@ pub fn check_tokens(
|
||||
|| settings.enabled.contains(&CheckCode::Q001)
|
||||
|| settings.enabled.contains(&CheckCode::Q002)
|
||||
|| settings.enabled.contains(&CheckCode::Q003);
|
||||
let enforce_commented_out_code = settings.enabled.contains(&CheckCode::ERA001);
|
||||
let enforce_invalid_escape_sequence = settings.enabled.contains(&CheckCode::W605);
|
||||
|
||||
let mut state_machine: StateMachine = Default::default();
|
||||
for (start, tok, end) in tokens.iter().flatten() {
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
let is_docstring = if enforce_ambiguous_unicode_character || enforce_quotes {
|
||||
state_machine.consume(tok)
|
||||
} else {
|
||||
@@ -72,6 +73,17 @@ pub fn check_tokens(
|
||||
}
|
||||
}
|
||||
|
||||
// eradicate
|
||||
if enforce_commented_out_code {
|
||||
if matches!(tok, Tok::Comment) {
|
||||
if let Some(check) =
|
||||
eradicate::checks::commented_out_code(locator, start, end, settings, autofix)
|
||||
{
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
@@ -81,4 +93,6 @@ pub fn check_tokens(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
|
||||
676
src/checks.rs
676
src/checks.rs
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
//! File automatically generated by examples/generate_check_code_prefix.rs.
|
||||
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::EnumString;
|
||||
@@ -59,10 +59,14 @@ pub enum CheckCodePrefix {
|
||||
B020,
|
||||
B021,
|
||||
B022,
|
||||
B023,
|
||||
B024,
|
||||
B025,
|
||||
B026,
|
||||
B027,
|
||||
B9,
|
||||
B90,
|
||||
B904,
|
||||
BLE,
|
||||
BLE0,
|
||||
BLE00,
|
||||
@@ -172,6 +176,10 @@ pub enum CheckCodePrefix {
|
||||
E902,
|
||||
E99,
|
||||
E999,
|
||||
ERA,
|
||||
ERA8,
|
||||
ERA80,
|
||||
ERA001,
|
||||
F,
|
||||
F4,
|
||||
F40,
|
||||
@@ -183,6 +191,22 @@ pub enum CheckCodePrefix {
|
||||
F406,
|
||||
F407,
|
||||
F5,
|
||||
F50,
|
||||
F501,
|
||||
F502,
|
||||
F503,
|
||||
F504,
|
||||
F505,
|
||||
F506,
|
||||
F507,
|
||||
F508,
|
||||
F509,
|
||||
F52,
|
||||
F521,
|
||||
F522,
|
||||
F523,
|
||||
F524,
|
||||
F525,
|
||||
F54,
|
||||
F541,
|
||||
F6,
|
||||
@@ -267,6 +291,9 @@ pub enum CheckCodePrefix {
|
||||
RUF001,
|
||||
RUF002,
|
||||
RUF003,
|
||||
RUF1,
|
||||
RUF10,
|
||||
RUF101,
|
||||
S,
|
||||
S1,
|
||||
S10,
|
||||
@@ -277,6 +304,9 @@ pub enum CheckCodePrefix {
|
||||
S106,
|
||||
S107,
|
||||
T,
|
||||
T1,
|
||||
T10,
|
||||
T100,
|
||||
T2,
|
||||
T20,
|
||||
T201,
|
||||
@@ -297,6 +327,8 @@ pub enum CheckCodePrefix {
|
||||
U011,
|
||||
U012,
|
||||
U013,
|
||||
U014,
|
||||
U015,
|
||||
W,
|
||||
W2,
|
||||
W29,
|
||||
@@ -333,6 +365,7 @@ pub enum PrefixSpecificity {
|
||||
|
||||
impl CheckCodePrefix {
|
||||
pub fn codes(&self) -> Vec<CheckCode> {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
CheckCodePrefix::A => vec![CheckCode::A001, CheckCode::A002, CheckCode::A003],
|
||||
CheckCodePrefix::A0 => vec![CheckCode::A001, CheckCode::A002, CheckCode::A003],
|
||||
@@ -406,10 +439,12 @@ impl CheckCodePrefix {
|
||||
CheckCode::B020,
|
||||
CheckCode::B021,
|
||||
CheckCode::B022,
|
||||
CheckCode::B023,
|
||||
CheckCode::B024,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
CheckCode::B027,
|
||||
CheckCode::B904,
|
||||
],
|
||||
CheckCodePrefix::B0 => vec![
|
||||
CheckCode::B002,
|
||||
@@ -433,6 +468,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::B020,
|
||||
CheckCode::B021,
|
||||
CheckCode::B022,
|
||||
CheckCode::B023,
|
||||
CheckCode::B024,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
@@ -482,6 +518,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::B020,
|
||||
CheckCode::B021,
|
||||
CheckCode::B022,
|
||||
CheckCode::B023,
|
||||
CheckCode::B024,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
@@ -490,10 +527,14 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B020 => vec![CheckCode::B020],
|
||||
CheckCodePrefix::B021 => vec![CheckCode::B021],
|
||||
CheckCodePrefix::B022 => vec![CheckCode::B022],
|
||||
CheckCodePrefix::B023 => vec![CheckCode::B023],
|
||||
CheckCodePrefix::B024 => vec![CheckCode::B024],
|
||||
CheckCodePrefix::B025 => vec![CheckCode::B025],
|
||||
CheckCodePrefix::B026 => vec![CheckCode::B026],
|
||||
CheckCodePrefix::B027 => vec![CheckCode::B027],
|
||||
CheckCodePrefix::B9 => vec![CheckCode::B904],
|
||||
CheckCodePrefix::B90 => vec![CheckCode::B904],
|
||||
CheckCodePrefix::B904 => vec![CheckCode::B904],
|
||||
CheckCodePrefix::BLE => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE0 => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE00 => vec![CheckCode::BLE001],
|
||||
@@ -826,6 +867,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::E902 => vec![CheckCode::E902],
|
||||
CheckCodePrefix::E99 => vec![CheckCode::E999],
|
||||
CheckCodePrefix::E999 => vec![CheckCode::E999],
|
||||
CheckCodePrefix::ERA => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::ERA8 => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::ERA80 => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::ERA001 => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::F => vec![
|
||||
CheckCode::F401,
|
||||
CheckCode::F402,
|
||||
@@ -834,6 +879,20 @@ impl CheckCodePrefix {
|
||||
CheckCode::F405,
|
||||
CheckCode::F406,
|
||||
CheckCode::F407,
|
||||
CheckCode::F501,
|
||||
CheckCode::F502,
|
||||
CheckCode::F503,
|
||||
CheckCode::F504,
|
||||
CheckCode::F505,
|
||||
CheckCode::F506,
|
||||
CheckCode::F507,
|
||||
CheckCode::F508,
|
||||
CheckCode::F509,
|
||||
CheckCode::F521,
|
||||
CheckCode::F522,
|
||||
CheckCode::F523,
|
||||
CheckCode::F524,
|
||||
CheckCode::F525,
|
||||
CheckCode::F541,
|
||||
CheckCode::F601,
|
||||
CheckCode::F602,
|
||||
@@ -881,7 +940,55 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F405 => vec![CheckCode::F405],
|
||||
CheckCodePrefix::F406 => vec![CheckCode::F406],
|
||||
CheckCodePrefix::F407 => vec![CheckCode::F407],
|
||||
CheckCodePrefix::F5 => vec![CheckCode::F541],
|
||||
CheckCodePrefix::F5 => vec![
|
||||
CheckCode::F501,
|
||||
CheckCode::F502,
|
||||
CheckCode::F503,
|
||||
CheckCode::F504,
|
||||
CheckCode::F505,
|
||||
CheckCode::F506,
|
||||
CheckCode::F507,
|
||||
CheckCode::F508,
|
||||
CheckCode::F509,
|
||||
CheckCode::F521,
|
||||
CheckCode::F522,
|
||||
CheckCode::F523,
|
||||
CheckCode::F524,
|
||||
CheckCode::F525,
|
||||
CheckCode::F541,
|
||||
],
|
||||
CheckCodePrefix::F50 => vec![
|
||||
CheckCode::F501,
|
||||
CheckCode::F502,
|
||||
CheckCode::F503,
|
||||
CheckCode::F504,
|
||||
CheckCode::F505,
|
||||
CheckCode::F506,
|
||||
CheckCode::F507,
|
||||
CheckCode::F508,
|
||||
CheckCode::F509,
|
||||
],
|
||||
CheckCodePrefix::F501 => vec![CheckCode::F501],
|
||||
CheckCodePrefix::F502 => vec![CheckCode::F502],
|
||||
CheckCodePrefix::F503 => vec![CheckCode::F503],
|
||||
CheckCodePrefix::F504 => vec![CheckCode::F504],
|
||||
CheckCodePrefix::F505 => vec![CheckCode::F505],
|
||||
CheckCodePrefix::F506 => vec![CheckCode::F506],
|
||||
CheckCodePrefix::F507 => vec![CheckCode::F507],
|
||||
CheckCodePrefix::F508 => vec![CheckCode::F508],
|
||||
CheckCodePrefix::F509 => vec![CheckCode::F509],
|
||||
CheckCodePrefix::F52 => vec![
|
||||
CheckCode::F521,
|
||||
CheckCode::F522,
|
||||
CheckCode::F523,
|
||||
CheckCode::F524,
|
||||
CheckCode::F525,
|
||||
],
|
||||
CheckCodePrefix::F521 => vec![CheckCode::F521],
|
||||
CheckCodePrefix::F522 => vec![CheckCode::F522],
|
||||
CheckCodePrefix::F523 => vec![CheckCode::F523],
|
||||
CheckCodePrefix::F524 => vec![CheckCode::F524],
|
||||
CheckCodePrefix::F525 => vec![CheckCode::F525],
|
||||
CheckCodePrefix::F54 => vec![CheckCode::F541],
|
||||
CheckCodePrefix::F541 => vec![CheckCode::F541],
|
||||
CheckCodePrefix::F6 => vec![
|
||||
@@ -1057,12 +1164,20 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::Q001 => vec![CheckCode::Q001],
|
||||
CheckCodePrefix::Q002 => vec![CheckCode::Q002],
|
||||
CheckCodePrefix::Q003 => vec![CheckCode::Q003],
|
||||
CheckCodePrefix::RUF => vec![CheckCode::RUF001, CheckCode::RUF002, CheckCode::RUF003],
|
||||
CheckCodePrefix::RUF => vec![
|
||||
CheckCode::RUF001,
|
||||
CheckCode::RUF002,
|
||||
CheckCode::RUF003,
|
||||
CheckCode::RUF101,
|
||||
],
|
||||
CheckCodePrefix::RUF0 => vec![CheckCode::RUF001, CheckCode::RUF002, CheckCode::RUF003],
|
||||
CheckCodePrefix::RUF00 => vec![CheckCode::RUF001, CheckCode::RUF002, CheckCode::RUF003],
|
||||
CheckCodePrefix::RUF001 => vec![CheckCode::RUF001],
|
||||
CheckCodePrefix::RUF002 => vec![CheckCode::RUF002],
|
||||
CheckCodePrefix::RUF003 => vec![CheckCode::RUF003],
|
||||
CheckCodePrefix::RUF1 => vec![CheckCode::RUF101],
|
||||
CheckCodePrefix::RUF10 => vec![CheckCode::RUF101],
|
||||
CheckCodePrefix::RUF101 => vec![CheckCode::RUF101],
|
||||
CheckCodePrefix::S => vec![
|
||||
CheckCode::S101,
|
||||
CheckCode::S102,
|
||||
@@ -1093,7 +1208,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::S105 => vec![CheckCode::S105],
|
||||
CheckCodePrefix::S106 => vec![CheckCode::S106],
|
||||
CheckCodePrefix::S107 => vec![CheckCode::S107],
|
||||
CheckCodePrefix::T => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T => vec![CheckCode::T100, CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T1 => vec![CheckCode::T100],
|
||||
CheckCodePrefix::T10 => vec![CheckCode::T100],
|
||||
CheckCodePrefix::T100 => vec![CheckCode::T100],
|
||||
CheckCodePrefix::T2 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T201 => vec![CheckCode::T201],
|
||||
@@ -1111,6 +1229,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
CheckCode::U013,
|
||||
CheckCode::U014,
|
||||
CheckCode::U015,
|
||||
],
|
||||
CheckCodePrefix::U0 => vec![
|
||||
CheckCode::U001,
|
||||
@@ -1125,6 +1245,8 @@ impl CheckCodePrefix {
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
CheckCode::U013,
|
||||
CheckCode::U014,
|
||||
CheckCode::U015,
|
||||
],
|
||||
CheckCodePrefix::U00 => vec![
|
||||
CheckCode::U001,
|
||||
@@ -1149,11 +1271,15 @@ impl CheckCodePrefix {
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
CheckCode::U013,
|
||||
CheckCode::U014,
|
||||
CheckCode::U015,
|
||||
],
|
||||
CheckCodePrefix::U010 => vec![CheckCode::U010],
|
||||
CheckCodePrefix::U011 => vec![CheckCode::U011],
|
||||
CheckCodePrefix::U012 => vec![CheckCode::U012],
|
||||
CheckCodePrefix::U013 => vec![CheckCode::U013],
|
||||
CheckCodePrefix::U014 => vec![CheckCode::U014],
|
||||
CheckCodePrefix::U015 => vec![CheckCode::U015],
|
||||
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
|
||||
CheckCodePrefix::W2 => vec![CheckCode::W292],
|
||||
CheckCodePrefix::W29 => vec![CheckCode::W292],
|
||||
@@ -1205,6 +1331,7 @@ impl CheckCodePrefix {
|
||||
|
||||
impl CheckCodePrefix {
|
||||
pub fn specificity(&self) -> PrefixSpecificity {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
CheckCodePrefix::A => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::A0 => PrefixSpecificity::Hundreds,
|
||||
@@ -1258,10 +1385,14 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B020 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B021 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B022 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B023 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B024 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B027 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B9 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::B90 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::B904 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::BLE => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::BLE0 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::BLE00 => PrefixSpecificity::Tens,
|
||||
@@ -1371,6 +1502,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::E902 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::E99 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::E999 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::ERA => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::ERA8 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::ERA80 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::ERA001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::F4 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::F40 => PrefixSpecificity::Tens,
|
||||
@@ -1382,6 +1517,22 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F406 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F407 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F5 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::F50 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F501 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F502 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F503 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F504 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F505 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F506 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F507 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F508 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F509 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F52 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F521 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F522 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F523 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F524 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F525 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F54 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F541 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F6 => PrefixSpecificity::Hundreds,
|
||||
@@ -1466,6 +1617,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::RUF001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::RUF002 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::RUF003 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::RUF1 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::RUF10 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::RUF101 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::S1 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::S10 => PrefixSpecificity::Tens,
|
||||
@@ -1476,6 +1630,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::S106 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S107 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::T => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::T1 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::T10 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::T100 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::T2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::T20 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::T201 => PrefixSpecificity::Explicit,
|
||||
@@ -1496,6 +1653,8 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U013 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U014 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U015 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::W => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::W29 => PrefixSpecificity::Tens,
|
||||
@@ -1523,3 +1682,26 @@ impl CheckCodePrefix {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::ANN,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
CheckCodePrefix::D,
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::ERA,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::FBT,
|
||||
CheckCodePrefix::I,
|
||||
CheckCodePrefix::M,
|
||||
CheckCodePrefix::N,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
];
|
||||
|
||||
31
src/cli.rs
31
src/cli.rs
@@ -1,19 +1,21 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{command, Parser};
|
||||
use fnv::FnvHashMap;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::logging::LogLevel;
|
||||
use crate::printer::SerializationFormat;
|
||||
use crate::settings::types::{PatternPrefixPair, PerFileIgnore, PythonVersion};
|
||||
use crate::settings::types::{FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, about = "Ruff: An extremely fast Python linter.")]
|
||||
#[command(version)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Cli {
|
||||
#[arg(required = true)]
|
||||
#[arg(required_unless_present_any = ["explain", "generate_shell_completion"])]
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Path to the `pyproject.toml` file to use for configuration.
|
||||
#[arg(long)]
|
||||
@@ -58,11 +60,11 @@ pub struct Cli {
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
/// List of paths, used to exclude files and/or directories from checks.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub exclude: Vec<String>,
|
||||
pub exclude: Vec<FilePattern>,
|
||||
/// Like --exclude, but adds additional files and directories on top of the
|
||||
/// excluded ones.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub extend_exclude: Vec<String>,
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
/// List of error codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
@@ -75,7 +77,7 @@ pub struct Cli {
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub per_file_ignores: Vec<PatternPrefixPair>,
|
||||
/// Output serialization format for error messages.
|
||||
#[arg(long, value_enum, default_value_t=SerializationFormat::Text)]
|
||||
#[arg(long, value_enum, default_value_t = SerializationFormat::Text)]
|
||||
pub format: SerializationFormat,
|
||||
/// Show violations with source code.
|
||||
#[arg(long)]
|
||||
@@ -109,6 +111,12 @@ pub struct Cli {
|
||||
/// The name of the file when passing it through stdin.
|
||||
#[arg(long)]
|
||||
pub stdin_filename: Option<String>,
|
||||
/// Explain a rule.
|
||||
#[arg(long)]
|
||||
pub explain: Option<CheckCode>,
|
||||
/// Generate shell completion
|
||||
#[arg(long, hide = true, value_name = "SHELL")]
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
@@ -143,11 +151,8 @@ pub fn extract_log_level(cli: &Cli) -> LogLevel {
|
||||
}
|
||||
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(
|
||||
pairs: Vec<PatternPrefixPair>,
|
||||
project_root: Option<&PathBuf>,
|
||||
) -> Vec<PerFileIgnore> {
|
||||
let mut per_file_ignores: FnvHashMap<String, Vec<CheckCodePrefix>> = FnvHashMap::default();
|
||||
pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgnore> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
|
||||
for pair in pairs {
|
||||
per_file_ignores
|
||||
.entry(pair.pattern)
|
||||
@@ -155,7 +160,7 @@ pub fn collect_per_file_ignores(
|
||||
.push(pair.prefix);
|
||||
}
|
||||
per_file_ignores
|
||||
.iter()
|
||||
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, prefixes, project_root))
|
||||
.into_iter()
|
||||
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, &prefixes))
|
||||
.collect()
|
||||
}
|
||||
|
||||
677
src/code_gen.rs
677
src/code_gen.rs
File diff suppressed because it is too large
Load Diff
66
src/commands.rs
Normal file
66
src/commands.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
use walkdir::DirEntry;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::fs::iter_python_files;
|
||||
use crate::printer::SerializationFormat;
|
||||
use crate::{Configuration, Settings};
|
||||
|
||||
/// Print the user-facing configuration settings.
|
||||
pub fn show_settings(
|
||||
configuration: &Configuration,
|
||||
project_root: Option<&PathBuf>,
|
||||
pyproject: Option<&PathBuf>,
|
||||
) {
|
||||
println!("Resolved configuration: {configuration:#?}");
|
||||
println!("Found project root at: {project_root:?}");
|
||||
println!("Found pyproject.toml at: {pyproject:?}");
|
||||
}
|
||||
|
||||
/// Show the list of files to be checked based on current settings.
|
||||
pub fn show_files(files: &[PathBuf], settings: &Settings) {
|
||||
let mut entries: Vec<DirEntry> = files
|
||||
.iter()
|
||||
.flat_map(|path| iter_python_files(path, &settings.exclude, &settings.extend_exclude))
|
||||
.flatten()
|
||||
.collect();
|
||||
entries.sort_by(|a, b| a.path().cmp(b.path()));
|
||||
for entry in entries {
|
||||
println!("{}", entry.path().to_string_lossy());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Explanation<'a> {
|
||||
code: &'a str,
|
||||
category: &'a str,
|
||||
summary: &'a str,
|
||||
}
|
||||
|
||||
/// Explain a `CheckCode` to the user.
|
||||
pub fn explain(code: &CheckCode, format: SerializationFormat) -> Result<()> {
|
||||
match format {
|
||||
SerializationFormat::Text => {
|
||||
println!(
|
||||
"{} ({}): {}",
|
||||
code.as_ref(),
|
||||
code.category().title(),
|
||||
code.kind().summary()
|
||||
);
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
code: code.as_ref(),
|
||||
category: code.category().title(),
|
||||
summary: &code.kind().summary(),
|
||||
})?
|
||||
);
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
@@ -11,8 +11,8 @@ use crate::{Settings, SourceCodeLocator};
|
||||
|
||||
bitflags! {
|
||||
pub struct Flags: u32 {
|
||||
const NOQA = 0b00000001;
|
||||
const ISORT = 0b00000010;
|
||||
const NOQA = 0b0000_0001;
|
||||
const ISORT = 0b0000_0010;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,18 +38,18 @@ pub struct Directives {
|
||||
pub fn extract_directives(
|
||||
lxr: &[LexResult],
|
||||
locator: &SourceCodeLocator,
|
||||
flags: &Flags,
|
||||
flags: Flags,
|
||||
) -> Directives {
|
||||
Directives {
|
||||
noqa_line_for: if flags.contains(Flags::NOQA) {
|
||||
extract_noqa_line_for(lxr)
|
||||
} else {
|
||||
Default::default()
|
||||
IntMap::default()
|
||||
},
|
||||
isort_exclusions: if flags.contains(Flags::ISORT) {
|
||||
extract_isort_exclusions(lxr, locator)
|
||||
} else {
|
||||
Default::default()
|
||||
IntSet::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -75,13 +75,13 @@ pub fn extract_noqa_line_for(lxr: &[LexResult]) -> IntMap<usize, usize> {
|
||||
/// Extract a set of lines over which to disable isort.
|
||||
pub fn extract_isort_exclusions(lxr: &[LexResult], locator: &SourceCodeLocator) -> IntSet<usize> {
|
||||
let mut exclusions: IntSet<usize> = IntSet::default();
|
||||
let mut off: Option<&Location> = None;
|
||||
for (start, tok, end) in lxr.iter().flatten() {
|
||||
let mut off: Option<Location> = None;
|
||||
for &(start, ref tok, end) in lxr.iter().flatten() {
|
||||
// TODO(charlie): Modify RustPython to include the comment text in the token.
|
||||
if matches!(tok, Tok::Comment) {
|
||||
let comment_text = locator.slice_source_code_range(&Range {
|
||||
location: *start,
|
||||
end_location: *end,
|
||||
location: start,
|
||||
end_location: end,
|
||||
});
|
||||
if off.is_some() {
|
||||
if comment_text == "# isort: on" {
|
||||
@@ -113,7 +113,7 @@ pub fn extract_isort_exclusions(lxr: &[LexResult], locator: &SourceCodeLocator)
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use nohash_hasher::IntMap;
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
@@ -121,8 +121,8 @@ mod tests {
|
||||
use crate::directives::extract_noqa_line_for;
|
||||
|
||||
#[test]
|
||||
fn extraction() -> Result<()> {
|
||||
let empty: IntMap<usize, usize> = Default::default();
|
||||
fn extraction() {
|
||||
let empty: IntMap<usize, usize> = IntMap::default();
|
||||
|
||||
let lxr: Vec<LexResult> = lexer::make_tokenizer(
|
||||
"x = 1
|
||||
@@ -200,7 +200,5 @@ z = x + 1",
|
||||
extract_noqa_line_for(&lxr),
|
||||
IntMap::from_iter([(2, 5), (3, 5), (4, 5)])
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
5
src/docstrings/constants.rs
Normal file
5
src/docstrings/constants.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
|
||||
];
|
||||
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
|
||||
@@ -1,10 +1,10 @@
|
||||
//! Abstractions for Google-style docstrings.
|
||||
|
||||
use fnv::FnvHashSet;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
|
||||
FnvHashSet::from_iter([
|
||||
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
|
||||
FxHashSet::from_iter([
|
||||
"Args",
|
||||
"Arguments",
|
||||
"Attention",
|
||||
@@ -36,36 +36,35 @@ pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::n
|
||||
])
|
||||
});
|
||||
|
||||
pub(crate) static LOWERCASE_GOOGLE_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> =
|
||||
Lazy::new(|| {
|
||||
FnvHashSet::from_iter([
|
||||
"args",
|
||||
"arguments",
|
||||
"attention",
|
||||
"attributes",
|
||||
"caution",
|
||||
"danger",
|
||||
"error",
|
||||
"example",
|
||||
"examples",
|
||||
"hint",
|
||||
"important",
|
||||
"keyword args",
|
||||
"keyword arguments",
|
||||
"methods",
|
||||
"note",
|
||||
"notes",
|
||||
"return",
|
||||
"returns",
|
||||
"raises",
|
||||
"references",
|
||||
"see also",
|
||||
"tip",
|
||||
"todo",
|
||||
"warning",
|
||||
"warnings",
|
||||
"warns",
|
||||
"yield",
|
||||
"yields",
|
||||
])
|
||||
});
|
||||
pub(crate) static LOWERCASE_GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
|
||||
FxHashSet::from_iter([
|
||||
"args",
|
||||
"arguments",
|
||||
"attention",
|
||||
"attributes",
|
||||
"caution",
|
||||
"danger",
|
||||
"error",
|
||||
"example",
|
||||
"examples",
|
||||
"hint",
|
||||
"important",
|
||||
"keyword args",
|
||||
"keyword arguments",
|
||||
"methods",
|
||||
"note",
|
||||
"notes",
|
||||
"return",
|
||||
"returns",
|
||||
"raises",
|
||||
"references",
|
||||
"see also",
|
||||
"tip",
|
||||
"todo",
|
||||
"warning",
|
||||
"warnings",
|
||||
"warns",
|
||||
"yield",
|
||||
"yields",
|
||||
])
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod constants;
|
||||
pub mod definition;
|
||||
pub mod extraction;
|
||||
pub mod google;
|
||||
pub mod helpers;
|
||||
pub mod numpy;
|
||||
pub mod sections;
|
||||
pub mod styles;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! Abstractions for NumPy-style docstrings.
|
||||
|
||||
use fnv::FnvHashSet;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
|
||||
FnvHashSet::from_iter([
|
||||
pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
|
||||
FxHashSet::from_iter([
|
||||
"short summary",
|
||||
"extended summary",
|
||||
"parameters",
|
||||
@@ -21,8 +21,8 @@ pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>>
|
||||
])
|
||||
});
|
||||
|
||||
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
|
||||
FnvHashSet::from_iter([
|
||||
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
|
||||
FxHashSet::from_iter([
|
||||
"Short Summary",
|
||||
"Extended Summary",
|
||||
"Parameters",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::docstrings::helpers;
|
||||
use crate::ast::whitespace;
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -14,7 +14,7 @@ pub(crate) struct SectionContext<'a> {
|
||||
fn suspected_as_section(line: &str, style: &SectionStyle) -> bool {
|
||||
style
|
||||
.lowercase_section_names()
|
||||
.contains(&helpers::leading_words(line).to_lowercase().as_str())
|
||||
.contains(&whitespace::leading_words(line).to_lowercase().as_str())
|
||||
}
|
||||
|
||||
/// Check if the suspected context is really a section header.
|
||||
@@ -64,7 +64,7 @@ pub(crate) fn section_contexts<'a>(
|
||||
let mut contexts = vec![];
|
||||
for lineno in suspected_section_indices {
|
||||
let context = SectionContext {
|
||||
section_name: helpers::leading_words(lines[lineno]),
|
||||
section_name: whitespace::leading_words(lines[lineno]),
|
||||
previous_line: lines[lineno - 1],
|
||||
line: lines[lineno],
|
||||
following_lines: &lines[lineno + 1..],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use fnv::FnvHashSet;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::docstrings::google::{GOOGLE_SECTION_NAMES, LOWERCASE_GOOGLE_SECTION_NAMES};
|
||||
use crate::docstrings::numpy::{LOWERCASE_NUMPY_SECTION_NAMES, NUMPY_SECTION_NAMES};
|
||||
@@ -10,14 +10,14 @@ pub(crate) enum SectionStyle {
|
||||
}
|
||||
|
||||
impl SectionStyle {
|
||||
pub(crate) fn section_names(&self) -> &Lazy<FnvHashSet<&'static str>> {
|
||||
pub(crate) fn section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
|
||||
match self {
|
||||
SectionStyle::NumPy => &NUMPY_SECTION_NAMES,
|
||||
SectionStyle::Google => &GOOGLE_SECTION_NAMES,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FnvHashSet<&'static str>> {
|
||||
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
|
||||
match self {
|
||||
SectionStyle::NumPy => &LOWERCASE_NUMPY_SECTION_NAMES,
|
||||
SectionStyle::Google => &LOWERCASE_GOOGLE_SECTION_NAMES,
|
||||
|
||||
51
src/eradicate/checks.rs
Normal file
51
src/eradicate/checks.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use rustpython_ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{CheckCode, CheckKind};
|
||||
use crate::eradicate::detection::comment_contains_code;
|
||||
use crate::{Check, Settings, SourceCodeLocator};
|
||||
|
||||
fn is_standalone_comment(line: &str) -> bool {
|
||||
for char in line.chars() {
|
||||
if char == '#' {
|
||||
return true;
|
||||
} else if !char.is_whitespace() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
unreachable!("Comment should contain '#' character")
|
||||
}
|
||||
|
||||
/// ERA001
|
||||
pub fn commented_out_code(
|
||||
locator: &SourceCodeLocator,
|
||||
start: Location,
|
||||
end: Location,
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
) -> Option<Check> {
|
||||
let location = Location::new(start.row(), 0);
|
||||
let end_location = Location::new(end.row() + 1, 0);
|
||||
let line = locator.slice_source_code_range(&Range {
|
||||
location,
|
||||
end_location,
|
||||
});
|
||||
|
||||
// Verify that the comment is on its own line, and that it contains code.
|
||||
if is_standalone_comment(&line) && comment_contains_code(&line) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::CommentedOutCode,
|
||||
Range {
|
||||
location: start,
|
||||
end_location: end,
|
||||
},
|
||||
);
|
||||
if autofix && settings.fixable.contains(&CheckCode::ERA001) {
|
||||
check.amend(Fix::deletion(location, end_location));
|
||||
}
|
||||
Some(check)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
223
src/eradicate/detection.rs
Normal file
223
src/eradicate/detection.rs
Normal file
@@ -0,0 +1,223 @@
|
||||
/// See: [eradicate.py](https://github.com/myint/eradicate/blob/98f199940979c94447a461d50d27862b118b282d/eradicate.py)
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
static ALLOWLIST_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
r"(?i)pylint|pyright|noqa|nosec|type:\s*ignore|fmt:\s*(on|off)|isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?])?)|TODO|FIXME|XXX"
|
||||
).unwrap()
|
||||
});
|
||||
static BRACKET_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[()\[\]{}\s]+$").unwrap());
|
||||
static CODE_INDICATORS: &[&str] = &[
|
||||
"(", ")", "[", "]", "{", "}", ":", "=", "%", "print", "return", "break", "continue", "import",
|
||||
];
|
||||
static CODE_KEYWORDS: Lazy<Vec<Regex>> = Lazy::new(|| {
|
||||
vec![
|
||||
Regex::new(r"^\s*elif\s+.*\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*else\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*try\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*finally\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*except\s+.*\s*:\s*$").unwrap(),
|
||||
]
|
||||
});
|
||||
static CODING_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)").unwrap());
|
||||
static HASH_NUMBER: Lazy<Regex> = Lazy::new(|| Regex::new(r"#\d").unwrap());
|
||||
static MULTILINE_ASSIGNMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^\s*\w+\s*=.*[(\[{]$").unwrap());
|
||||
static PARTIAL_DICTIONARY_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r#"^\s*['"]\w+['"]\s*:.+[,{]\s*$"#).unwrap());
|
||||
static PRINT_RETURN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(print|return)\b\s*").unwrap());
|
||||
|
||||
/// Returns `true` if a comment contains Python code.
|
||||
pub fn comment_contains_code(line: &str) -> bool {
|
||||
let line = if let Some(line) = line.trim().strip_prefix('#') {
|
||||
line.trim()
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Ignore non-comment related hashes (e.g., "# Issue #999").
|
||||
if HASH_NUMBER.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore whitelisted comments.
|
||||
if ALLOWLIST_REGEX.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if CODING_COMMENT_REGEX.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that this is possibly code.
|
||||
if CODE_INDICATORS.iter().all(|symbol| !line.contains(symbol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if multiline_case(line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if CODE_KEYWORDS.iter().any(|symbol| symbol.is_match(line)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let line = PRINT_RETURN_REGEX.replace_all(line, "");
|
||||
|
||||
if PARTIAL_DICTIONARY_REGEX.is_match(&line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finally, compile the source code.
|
||||
rustpython_parser::parser::parse_program(&line, "<filename>").is_ok()
|
||||
}
|
||||
|
||||
/// Returns `true` if a line is probably part of some multiline code.
|
||||
fn multiline_case(line: &str) -> bool {
|
||||
if line.ends_with('\\') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if MULTILINE_ASSIGNMENT_REGEX.is_match(line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if BRACKET_REGEX.is_match(line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::eradicate::detection::comment_contains_code;
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_basic() {
|
||||
assert!(comment_contains_code("# x = 1"));
|
||||
assert!(comment_contains_code("#from foo import eradicate"));
|
||||
assert!(comment_contains_code("#import eradicate"));
|
||||
assert!(comment_contains_code(r#"#"key": value,"#));
|
||||
assert!(comment_contains_code(r#"#"key": "value","#));
|
||||
assert!(comment_contains_code(r#"#"key": 1 + 1,"#));
|
||||
assert!(comment_contains_code("#'key': 1 + 1,"));
|
||||
assert!(comment_contains_code(r#"#"key": {"#));
|
||||
assert!(comment_contains_code("#}"));
|
||||
assert!(comment_contains_code("#} )]"));
|
||||
|
||||
assert!(!comment_contains_code("#"));
|
||||
assert!(!comment_contains_code("# This is a (real) comment."));
|
||||
assert!(!comment_contains_code("# 123"));
|
||||
assert!(!comment_contains_code("# 123.1"));
|
||||
assert!(!comment_contains_code("# 1, 2, 3"));
|
||||
assert!(!comment_contains_code("x = 1 # x = 1"));
|
||||
assert!(!comment_contains_code(
|
||||
"# pylint: disable=redefined-outer-name"
|
||||
));
|
||||
assert!(!comment_contains_code("# Issue #999: This is not code"));
|
||||
|
||||
// TODO(charlie): This should be `true` under aggressive mode.
|
||||
assert!(!comment_contains_code("#},"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_print() {
|
||||
assert!(comment_contains_code("#print"));
|
||||
assert!(comment_contains_code("#print(1)"));
|
||||
assert!(comment_contains_code("#print 1"));
|
||||
|
||||
assert!(!comment_contains_code("#to print"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_return() {
|
||||
assert!(comment_contains_code("#return x"));
|
||||
|
||||
assert!(!comment_contains_code("#to print"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_multiline() {
|
||||
assert!(comment_contains_code("#else:"));
|
||||
assert!(comment_contains_code("# else : "));
|
||||
assert!(comment_contains_code(r#"# "foo %d" % \\"#));
|
||||
assert!(comment_contains_code("#elif True:"));
|
||||
assert!(comment_contains_code("#x = foo("));
|
||||
assert!(comment_contains_code("#except Exception:"));
|
||||
|
||||
assert!(!comment_contains_code("# this is = to that :("));
|
||||
assert!(!comment_contains_code("#else"));
|
||||
assert!(!comment_contains_code("#or else:"));
|
||||
assert!(!comment_contains_code("#else True:"));
|
||||
|
||||
// TODO(charlie): This should be `true` under aggressive mode.
|
||||
assert!(!comment_contains_code("#def foo():"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_sentences() {
|
||||
assert!(!comment_contains_code("#code is good"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_encoding() {
|
||||
assert!(comment_contains_code("# codings=utf-8"));
|
||||
|
||||
assert!(!comment_contains_code("# coding=utf-8"));
|
||||
assert!(!comment_contains_code("#coding= utf-8"));
|
||||
assert!(!comment_contains_code("# coding: utf-8"));
|
||||
assert!(!comment_contains_code("# encoding: utf8"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_default_allowlist() {
|
||||
assert!(!comment_contains_code("# pylint: disable=A0123"));
|
||||
assert!(!comment_contains_code("# pylint:disable=A0123"));
|
||||
assert!(!comment_contains_code("# pylint: disable = A0123"));
|
||||
assert!(!comment_contains_code("# pylint:disable = A0123"));
|
||||
assert!(!comment_contains_code("# pyright: reportErrorName=true"));
|
||||
assert!(!comment_contains_code("# noqa"));
|
||||
assert!(!comment_contains_code("# NOQA"));
|
||||
assert!(!comment_contains_code("# noqa: A123"));
|
||||
assert!(!comment_contains_code("# noqa:A123"));
|
||||
assert!(!comment_contains_code("# nosec"));
|
||||
assert!(!comment_contains_code("# fmt: on"));
|
||||
assert!(!comment_contains_code("# fmt: off"));
|
||||
assert!(!comment_contains_code("# fmt:on"));
|
||||
assert!(!comment_contains_code("# fmt:off"));
|
||||
assert!(!comment_contains_code("# isort: on"));
|
||||
assert!(!comment_contains_code("# isort:on"));
|
||||
assert!(!comment_contains_code("# isort: off"));
|
||||
assert!(!comment_contains_code("# isort:off"));
|
||||
assert!(!comment_contains_code("# isort: skip"));
|
||||
assert!(!comment_contains_code("# isort:skip"));
|
||||
assert!(!comment_contains_code("# isort: skip_file"));
|
||||
assert!(!comment_contains_code("# isort:skip_file"));
|
||||
assert!(!comment_contains_code("# isort: split"));
|
||||
assert!(!comment_contains_code("# isort:split"));
|
||||
assert!(!comment_contains_code("# isort: dont-add-imports"));
|
||||
assert!(!comment_contains_code("# isort:dont-add-imports"));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports: [\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort:dont-add-imports: [\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports:[\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort:dont-add-imports:[\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code("# type: ignore"));
|
||||
assert!(!comment_contains_code("# type:ignore"));
|
||||
assert!(!comment_contains_code("# type: ignore[import]"));
|
||||
assert!(!comment_contains_code("# type:ignore[import]"));
|
||||
assert!(!comment_contains_code("# TODO: Do that"));
|
||||
assert!(!comment_contains_code("# FIXME: Fix that"));
|
||||
assert!(!comment_contains_code("# XXX: What ever"));
|
||||
}
|
||||
}
|
||||
2
src/eradicate/mod.rs
Normal file
2
src/eradicate/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod checks;
|
||||
pub mod detection;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user