Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8d46d09ef | ||
|
|
294cd95c54 | ||
|
|
d8e709648d | ||
|
|
52cc4d6537 | ||
|
|
08e9d12137 | ||
|
|
39fdc71b49 | ||
|
|
6b0736cf4b | ||
|
|
58269a918a | ||
|
|
9168a12679 | ||
|
|
cb971d3a48 | ||
|
|
57a5071b4e | ||
|
|
976fe364d4 | ||
|
|
028c7855b2 | ||
|
|
e5179f67fd | ||
|
|
c9c199dbca | ||
|
|
70e378b736 | ||
|
|
ca49b00e55 | ||
|
|
f661c90bd7 | ||
|
|
5a84df293f | ||
|
|
23d9309111 | ||
|
|
98ea94fdb7 | ||
|
|
746e1d3436 | ||
|
|
016ff01a04 | ||
|
|
298498e934 | ||
|
|
3ef1c2e303 | ||
|
|
ac028cd9f8 | ||
|
|
c0eb5c28d1 | ||
|
|
a77b4566e4 | ||
|
|
860993187e | ||
|
|
58d4e00604 | ||
|
|
2d95912699 | ||
|
|
4f927fbacc | ||
|
|
2e41301520 | ||
|
|
3179fc110d | ||
|
|
03ae0118b7 | ||
|
|
05176890ee | ||
|
|
849b947b3e | ||
|
|
c314e10e54 | ||
|
|
9eda286dcd | ||
|
|
65a3461519 | ||
|
|
1b8d2df3bf | ||
|
|
179ead0157 | ||
|
|
d451c7a506 | ||
|
|
502ce80c91 | ||
|
|
49d22d8fe2 | ||
|
|
08e0b76587 | ||
|
|
f7515739ac | ||
|
|
53e810ed3e | ||
|
|
66a195f805 | ||
|
|
b8483975a4 | ||
|
|
4dd2032687 | ||
|
|
c6c15d5cf9 | ||
|
|
2bf7b35268 | ||
|
|
6d1adc85fc | ||
|
|
02285c18d1 | ||
|
|
8120d7c974 | ||
|
|
c858804ed4 | ||
|
|
b9d075c252 | ||
|
|
7627e840c9 | ||
|
|
3c03e2cb2e | ||
|
|
aeae63b7ea | ||
|
|
7be17c5f1e | ||
|
|
6128346b08 | ||
|
|
1705574e75 | ||
|
|
15f65fa8d6 | ||
|
|
d1cf0ee52b | ||
|
|
32520ff07f | ||
|
|
3659236580 | ||
|
|
dde69d50b5 | ||
|
|
67198ce7b3 | ||
|
|
c21a5912b9 | ||
|
|
48a5cd1dd9 | ||
|
|
63f3d5e610 | ||
|
|
7dab4807d0 | ||
|
|
83f6e52c92 | ||
|
|
5ce7ce5bc3 | ||
|
|
749d197119 | ||
|
|
46c184600f | ||
|
|
e2051ef72f | ||
|
|
1abaece9ed | ||
|
|
8b35b052b8 | ||
|
|
5a34504149 | ||
|
|
0e53ddc2b3 | ||
|
|
1f07ad6e61 | ||
|
|
1666e8ba1e | ||
|
|
c399b3e6c1 | ||
|
|
9089ef74bc | ||
|
|
28c9263722 | ||
|
|
fc4c927788 | ||
|
|
26f39cac2f | ||
|
|
02897a141b | ||
|
|
fc465cc2af | ||
|
|
ca8a122889 | ||
|
|
6769a5bce7 | ||
|
|
fda93c6245 | ||
|
|
099d5414f2 | ||
|
|
9ddd5e4cfe | ||
|
|
b8835c2e35 | ||
|
|
1d4422f004 | ||
|
|
2dccb7611a | ||
|
|
f8ac6d7bf0 | ||
|
|
0123425be1 | ||
|
|
c53f91d943 | ||
|
|
4a12ebb9b1 | ||
|
|
0e4d5eeea7 | ||
|
|
bbe44360e8 | ||
|
|
37e80d98ab | ||
|
|
306393063d | ||
|
|
f5a3c90288 | ||
|
|
8289ede00f | ||
|
|
77e65c9ff5 | ||
|
|
d827a9156e | ||
|
|
418808895e | ||
|
|
ac4e212ed2 | ||
|
|
551b810aeb | ||
|
|
1b61d4e18b | ||
|
|
752c0150e1 | ||
|
|
81651a8479 | ||
|
|
86d0749ed7 | ||
|
|
19fc410683 | ||
|
|
5a70a573cd | ||
|
|
74731a3456 | ||
|
|
863e39fe5f | ||
|
|
d0f9ee33ec | ||
|
|
1cf3d880a7 | ||
|
|
97dcb738fa | ||
|
|
ffb4e89a98 | ||
|
|
43b7ee215c | ||
|
|
77099dcd4d | ||
|
|
70ff65154d | ||
|
|
7db6a2d6d4 | ||
|
|
42924c0d9a | ||
|
|
31d00936ee | ||
|
|
c3c5d9a852 | ||
|
|
7e5c19385c | ||
|
|
5b54325c81 | ||
|
|
e6538a7969 | ||
|
|
24faabf1f4 | ||
|
|
9b0a160239 | ||
|
|
9fd29e2c54 | ||
|
|
e83ed0ecba | ||
|
|
dadbfea497 | ||
|
|
9f84c497f9 | ||
|
|
0ec25d1514 | ||
|
|
6a87c99004 | ||
|
|
c8f60c9588 | ||
|
|
113610a8d4 | ||
|
|
6376e5915e | ||
|
|
3d8fb5be20 | ||
|
|
0040991778 | ||
|
|
acb70520f8 | ||
|
|
6eb9268675 | ||
|
|
e5f5142e3e | ||
|
|
98d5ffb817 | ||
|
|
3f20f73413 | ||
|
|
a5e42d2f7c | ||
|
|
0bc1f68111 | ||
|
|
d2b09d77c5 | ||
|
|
0377834f9f | ||
|
|
3d650f9dd6 | ||
|
|
a72590ecde | ||
|
|
812b227334 | ||
|
|
6f58717ba4 | ||
|
|
8aab96fb9e | ||
|
|
9e6f7153a9 | ||
|
|
cda2ff0b18 | ||
|
|
ec63658250 | ||
|
|
1a97de0b01 | ||
|
|
1cbe48522e | ||
|
|
bfbde537af | ||
|
|
cba91b758b | ||
|
|
0bab642f5a | ||
|
|
bd09a1819f | ||
|
|
682d206992 | ||
|
|
c32441e4ab | ||
|
|
6f16f1c39b | ||
|
|
9011456aa1 | ||
|
|
fa191cceeb | ||
|
|
ac6c3affdd | ||
|
|
9a018c1650 | ||
|
|
0aef5c67a3 | ||
|
|
a048594416 | ||
|
|
5437f1299b | ||
|
|
41c0608a69 | ||
|
|
eb0d42187f | ||
|
|
48daa0f0ca | ||
|
|
417fe4355f | ||
|
|
a129181407 | ||
|
|
fc628de667 | ||
|
|
9e2418097c | ||
|
|
d4e5639aaf | ||
|
|
67e58a024a | ||
|
|
233be0e074 | ||
|
|
7750087f56 | ||
|
|
7d5fb0de8a | ||
|
|
8a98cfc4b8 | ||
|
|
54d1719424 | ||
|
|
0f622f0126 | ||
|
|
739a92e99d | ||
|
|
5a07c9f57c | ||
|
|
31027497c6 | ||
|
|
dabfdf718e | ||
|
|
5829bae976 | ||
|
|
ff3665a24b |
14
.github/workflows/ci.yaml
vendored
14
.github/workflows/ci.yaml
vendored
@@ -40,9 +40,20 @@ jobs:
|
||||
run: rustup component add rustfmt
|
||||
- run: cargo fmt --all --check
|
||||
|
||||
cargo_clippy:
|
||||
cargo-clippy:
|
||||
name: "cargo clippy"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: |
|
||||
rustup component add clippy
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
cargo-clippy-wasm:
|
||||
name: "cargo clippy (wasm)"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -50,7 +61,6 @@ jobs:
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
|
||||
cargo-test:
|
||||
|
||||
13
.github/workflows/docs.yaml
vendored
13
.github/workflows/docs.yaml
vendored
@@ -1,12 +1,8 @@
|
||||
name: mkdocs
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- README.md
|
||||
- mkdocs.template.yml
|
||||
- .github/workflows/docs.yaml
|
||||
branches: [main]
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -17,9 +13,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
pip install "mkdocs~=1.4.2" "mkdocs-material~=9.0.6"
|
||||
pip install -r docs/requirements.txt
|
||||
- name: "Copy README File"
|
||||
run: |
|
||||
python scripts/transform_readme.py --target mkdocs
|
||||
|
||||
49
.github/workflows/flake8-to-ruff.yaml
vendored
49
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels - universal2"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: "Install built wheel - universal2"
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -201,7 +201,7 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -222,40 +222,6 @@ jobs:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
pypy:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
target: [x86_64, aarch64]
|
||||
python-version:
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
target: aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: pypy${{ matrix.python-version }}
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -i pypy${{ matrix.python-version }} -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
@@ -267,7 +233,6 @@ jobs:
|
||||
- linux-cross
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
- pypy
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
|
||||
176
.github/workflows/ruff.yaml
vendored
176
.github/workflows/ruff.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist
|
||||
@@ -41,6 +41,19 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
macos-universal:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
@@ -52,7 +65,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - universal2"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist
|
||||
- name: "Install built wheel - universal2"
|
||||
@@ -63,26 +76,45 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x64, x86]
|
||||
platform:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
arch: x64
|
||||
- target: i686-pc-windows-msvc
|
||||
arch: x86
|
||||
- target: aarch64-pc-windows-msvc
|
||||
arch: x64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.target }}
|
||||
architecture: ${{ matrix.platform.arch }}
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
@@ -91,12 +123,27 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.zip
|
||||
*.sha256
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, i686]
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- i686-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -106,13 +153,13 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
if: ${{ startsWith(matrix.target, 'x86_64') }}
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: "Upload wheels"
|
||||
@@ -120,12 +167,34 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
linux-cross:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64, armv7, s390x, ppc64le, ppc64]
|
||||
platform:
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
arch: aarch64
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
arch: armv7
|
||||
- target: s390x-unknown-linux-gnu
|
||||
arch: s390x
|
||||
- target: powerpc64le-unknown-linux-gnu
|
||||
arch: ppc64le
|
||||
- target: powerpc64-unknown-linux-gnu
|
||||
arch: ppc64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -134,16 +203,16 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2.5.0
|
||||
if: matrix.target != 'ppc64'
|
||||
if: matrix.platform.arch != 'ppc64'
|
||||
name: Install built wheel
|
||||
with:
|
||||
arch: ${{ matrix.target }}
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
distro: ubuntu20.04
|
||||
githubToken: ${{ github.token }}
|
||||
install: |
|
||||
@@ -157,6 +226,18 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
musllinux:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -174,7 +255,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -193,6 +274,18 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
musllinux-cross:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -211,7 +304,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -231,42 +324,18 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
pypy:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
target: [x86_64, aarch64]
|
||||
python-version:
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
target: aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: pypy${{ matrix.python-version }}
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -i pypy${{ matrix.python-version }}
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: "Upload wheels"
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
release:
|
||||
name: Release
|
||||
@@ -279,7 +348,6 @@ jobs:
|
||||
- linux-cross
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
- pypy
|
||||
if: "startsWith(github.ref, 'refs/tags/')"
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
@@ -296,3 +364,11 @@ jobs:
|
||||
- name: "Update pre-commit mirror"
|
||||
run: |
|
||||
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.RUFF_PRE_COMMIT_PAT }}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/charliermarsh/ruff-pre-commit/dispatches --data '{"event_type": "pypi_release"}'
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: binaries/*
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,8 +1,6 @@
|
||||
# Local cache
|
||||
.ruff_cache
|
||||
crates/ruff/resources/test/cpython
|
||||
docs/*
|
||||
!docs/rules
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
|
||||
@@ -189,3 +187,7 @@ cython_debug/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# VIM
|
||||
.*.sw?
|
||||
.sw?
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.246
|
||||
|
||||
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/charliermarsh/ruff/pull/2773))
|
||||
|
||||
This rule was introduced in v0.0.245. However, it turns out that pycodestyle and Flake8 ignore this
|
||||
rule by default, as it is not part of PEP 8. As such, we've removed it from Ruff.
|
||||
|
||||
## 0.0.245
|
||||
|
||||
### Ruff's public `check` method was removed ([#2709](https://github.com/charliermarsh/ruff/pull/2709))
|
||||
|
||||
Previously, Ruff exposed a `check` method as a public Rust API. This method was used by few,
|
||||
if any clients, and was not well documented or supported. As such, it has been removed, with
|
||||
the intention of adding a stable public API in the future.
|
||||
|
||||
## 0.0.238
|
||||
|
||||
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/charliermarsh/ruff/pull/2312))
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
- [Our Pledge](#our-pledge)
|
||||
- [Our Standards](#our-standards)
|
||||
- [Enforcement Responsibilities](#enforcement-responsibilities)
|
||||
- [Scope](#scope)
|
||||
- [Enforcement](#enforcement)
|
||||
- [Enforcement Guidelines](#enforcement-guidelines)
|
||||
- [1. Correction](#1-correction)
|
||||
- [2. Warning](#2-warning)
|
||||
- [3. Temporary Ban](#3-temporary-ban)
|
||||
- [4. Permanent Ban](#4-permanent-ban)
|
||||
- [Attribution](#attribution)
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
|
||||
@@ -2,7 +2,18 @@
|
||||
|
||||
Welcome! We're happy to have you here. Thank you in advance for your contribution to Ruff.
|
||||
|
||||
## The basics
|
||||
- [The Basics](#the-basics)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Development](#development)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
|
||||
- [Rule naming convention](#rule-naming-convention)
|
||||
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
|
||||
- [MkDocs](#mkdocs)
|
||||
- [Release Process](#release-process)
|
||||
- [Benchmarks](#benchmarks)
|
||||
|
||||
## The Basics
|
||||
|
||||
Ruff welcomes contributions in the form of Pull Requests.
|
||||
|
||||
@@ -22,6 +33,9 @@ As a concrete example: consider taking on one of the rules from the [`tryceratop
|
||||
plugin, and looking to the originating [Python source](https://github.com/guilatrova/tryceratops)
|
||||
for guidance.
|
||||
|
||||
Alternatively, we've started work on the [`flake8-pyi`](https://github.com/charliermarsh/ruff/issues/848)
|
||||
plugin (see the [Python source](https://github.com/PyCQA/flake8-pyi)) -- another good place to start.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Ruff is written in Rust. You'll need to install the
|
||||
@@ -70,7 +84,7 @@ pre-commit run --all-files
|
||||
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
|
||||
prior to merging.
|
||||
|
||||
### Project structure
|
||||
### Project Structure
|
||||
|
||||
Ruff is structured as a monorepo with a [flat crate structure](https://matklad.github.io/2021/08/22/large-rust-workspaces.html),
|
||||
such that all crates are contained in a flat `crates` directory.
|
||||
@@ -91,15 +105,16 @@ At time of writing, the repository includes the following crates:
|
||||
|
||||
At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
2. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
|
||||
3. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
|
||||
4. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
|
||||
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
|
||||
2. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
3. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
|
||||
4. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
|
||||
5. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
|
||||
checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks), `crates/ruff/src/checkers/lines.rs`
|
||||
(for text-based checks), or `crates/ruff/src/checkers/filesystem.rs` (for filesystem-based
|
||||
checks).
|
||||
5. Add a test fixture.
|
||||
6. Update the generated files (documentation and generated code).
|
||||
6. Add a test fixture.
|
||||
7. Update the generated files (documentation and generated code).
|
||||
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
@@ -129,6 +144,17 @@ generated snapshot, then commit the snapshot file alongside the rest of your cha
|
||||
|
||||
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
|
||||
|
||||
#### Rule naming convention
|
||||
|
||||
The rule name should make sense when read as "allow *rule-name*" or "allow *rule-name* items".
|
||||
|
||||
This implies that rule names:
|
||||
|
||||
* should state the bad thing being checked for
|
||||
|
||||
* should not contain instructions on what you what you should use instead
|
||||
(these belong in the rule documentation and the `autofix_title` for rules that have autofix)
|
||||
|
||||
### Example: Adding a new configuration option
|
||||
|
||||
Ruff's user-facing settings live in a few different places.
|
||||
@@ -155,7 +181,27 @@ lives in `crates/ruff/src/flake8_to_ruff/converter.rs`.
|
||||
|
||||
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
|
||||
|
||||
## Release process
|
||||
## MkDocs
|
||||
|
||||
To preview any changes to the documentation locally:
|
||||
|
||||
1. Install MkDocs and Material for MkDocs with:
|
||||
```shell
|
||||
pip install -r docs/requirements.txt
|
||||
```
|
||||
2. Generate the MkDocs site with:
|
||||
```shell
|
||||
python scripts/generate_mkdocs.py
|
||||
```
|
||||
3. Run the development server with:
|
||||
```shell
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
The documentation should then be available locally at
|
||||
[http://127.0.0.1:8000/docs/](http://127.0.0.1:8000/docs/).
|
||||
|
||||
## Release Process
|
||||
|
||||
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub
|
||||
Actions, which automatically generates the appropriate wheels across architectures and publishes
|
||||
|
||||
1021
Cargo.lock
generated
1021
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
@@ -2,6 +2,23 @@
|
||||
members = ["crates/*"]
|
||||
default-members = ["crates/ruff", "crates/ruff_cli"]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
regex = { version = "1.6.0" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "61b48f108982d865524f86624a9d5bc2ae3bccef" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "61b48f108982d865524f86624a9d5bc2ae3bccef" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
toml = { version = "0.6.0" }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = "thin"
|
||||
|
||||
79
LICENSE
79
LICENSE
@@ -245,6 +245,31 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-pyi, licensed as follows:
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Łukasz Langa
|
||||
|
||||
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-print, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
@@ -1035,3 +1060,57 @@ are:
|
||||
"""
|
||||
Freely Distributable
|
||||
"""
|
||||
|
||||
- flake8-django, licensed under the GPL license.
|
||||
|
||||
- rust-analyzer/text-size, licensed under the MIT license:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
- rome/tools, licensed under the MIT license:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) Rome Tools, Inc. and its affiliates.
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
430
README.md
430
README.md
@@ -7,7 +7,6 @@
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://pypi.python.org/pypi/ruff)
|
||||
[](https://github.com/charliermarsh/ruff/actions)
|
||||
[](https://info.jetbrains.com/PyCharm-Webinar-February14-2023.html)
|
||||
|
||||
[**Discord**](https://discord.gg/c9MhzV8aU5) | [**Docs**](https://beta.ruff.rs/docs/) | [**Playground**](https://play.ruff.rs/)
|
||||
|
||||
@@ -27,7 +26,6 @@ An extremely fast Python linter, written in Rust.
|
||||
|
||||
* ⚡️ 10-100x faster than existing linters
|
||||
* 🐍 Installable via `pip`
|
||||
* 🤝 Python 3.11 compatibility
|
||||
* 🛠️ `pyproject.toml` support
|
||||
* 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
* 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
@@ -49,10 +47,11 @@ Ruff is extremely actively developed and used in major open-source projects like
|
||||
|
||||
* [pandas](https://github.com/pandas-dev/pandas)
|
||||
* [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
* [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
|
||||
* [Apache Airflow](https://github.com/apache/airflow)
|
||||
* [SciPy](https://github.com/scipy/scipy)
|
||||
* [Bokeh](https://github.com/bokeh/bokeh)
|
||||
* [Zulip](https://github.com/zulip/zulip)
|
||||
* [Bokeh](https://github.com/bokeh/bokeh)
|
||||
* [Pydantic](https://github.com/pydantic/pydantic)
|
||||
* [Dagster](https://github.com/dagster-io/dagster)
|
||||
* [Dagger](https://github.com/dagger/dagger)
|
||||
@@ -137,6 +136,7 @@ This README is also available as [documentation](https://beta.ruff.rs/docs/).
|
||||
1. [flake8-comprehensions (C4)](#flake8-comprehensions-c4)
|
||||
1. [flake8-datetimez (DTZ)](#flake8-datetimez-dtz)
|
||||
1. [flake8-debugger (T10)](#flake8-debugger-t10)
|
||||
1. [flake8-django (DJ)](#flake8-django-dj)
|
||||
1. [flake8-errmsg (EM)](#flake8-errmsg-em)
|
||||
1. [flake8-executable (EXE)](#flake8-executable-exe)
|
||||
1. [flake8-implicit-str-concat (ISC)](#flake8-implicit-str-concat-isc)
|
||||
@@ -145,9 +145,12 @@ This README is also available as [documentation](https://beta.ruff.rs/docs/).
|
||||
1. [flake8-no-pep420 (INP)](#flake8-no-pep420-inp)
|
||||
1. [flake8-pie (PIE)](#flake8-pie-pie)
|
||||
1. [flake8-print (T20)](#flake8-print-t20)
|
||||
1. [flake8-pyi (PYI)](#flake8-pyi-pyi)
|
||||
1. [flake8-pytest-style (PT)](#flake8-pytest-style-pt)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes-q)
|
||||
1. [flake8-raise (RSE)](#flake8-raise-rse)
|
||||
1. [flake8-return (RET)](#flake8-return-ret)
|
||||
1. [flake8-self (SLF)](#flake8-self-slf)
|
||||
1. [flake8-simplify (SIM)](#flake8-simplify-sim)
|
||||
1. [flake8-tidy-imports (TID)](#flake8-tidy-imports-tid)
|
||||
1. [flake8-type-checking (TCH)](#flake8-type-checking-tch)
|
||||
@@ -158,14 +161,14 @@ This README is also available as [documentation](https://beta.ruff.rs/docs/).
|
||||
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
||||
1. [Pylint (PL)](#pylint-pl)
|
||||
1. [tryceratops (TRY)](#tryceratops-try)
|
||||
1. [flake8-raise (RSE)](#flake8-raise-rse)
|
||||
1. [flake8-self (SLF)](#flake8-self-slf)
|
||||
1. [NumPy-specific rules (NPY)](#numpy-specific-rules-npy)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
|
||||
1. [Editor Integrations](#editor-integrations)
|
||||
1. [FAQ](#faq)
|
||||
1. [Contributing](#contributing)
|
||||
1. [Support](#support)
|
||||
1. [Reference](#reference)
|
||||
1. [Acknowledgements](#acknowledgements)
|
||||
1. [License](#license)
|
||||
|
||||
## Installation and Usage
|
||||
@@ -230,20 +233,33 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.244'
|
||||
rev: 'v0.0.247'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
|
||||
Or, to enable autofix:
|
||||
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.247'
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
```
|
||||
|
||||
<!-- End section: Installation and Usage -->
|
||||
|
||||
## Configuration
|
||||
|
||||
<!-- Begin section: Configuration -->
|
||||
|
||||
Ruff is configurable both via `pyproject.toml` and the command line. For a full list of configurable
|
||||
Ruff is configurable both via `pyproject.toml`, `ruff.toml`, and the command line. For a full list of configurable
|
||||
options, see the [API reference](#reference).
|
||||
|
||||
### Configure via `pyproject.toml`
|
||||
|
||||
If left unspecified, the default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
@@ -349,6 +365,8 @@ If you're wondering how to configure Ruff, here are some **recommended guideline
|
||||
* By default, Ruff's autofix is aggressive. If you find that it's too aggressive for your liking,
|
||||
consider turning off autofix for specific rules or categories (see: [FAQ](#ruff-tried-to-fix-something-but-it-broke-my-code-what-should-i-do)).
|
||||
|
||||
### Configure via `ruff.toml`
|
||||
|
||||
As an alternative to `pyproject.toml`, Ruff will also respect a `ruff.toml` file, which implements
|
||||
an equivalent schema (though the `[tool.ruff]` hierarchy can be omitted). For example, the
|
||||
`pyproject.toml` described above would be represented via the following `ruff.toml`:
|
||||
@@ -392,6 +410,7 @@ Usage: ruff [OPTIONS] <COMMAND>
|
||||
Commands:
|
||||
check Run Ruff on the given files or directories (default)
|
||||
rule Explain a rule
|
||||
config List or describe the available configuration options
|
||||
linter List all supported upstream linters
|
||||
clean Clear any caches in the current directory and any subdirectories
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
@@ -423,18 +442,34 @@ Arguments:
|
||||
[FILES]... List of files or directories to check
|
||||
|
||||
Options:
|
||||
--fix Attempt to automatically fix lint violations
|
||||
--show-source Show violations with source code
|
||||
--diff Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
|
||||
-w, --watch Run in watch mode by re-running whenever files change
|
||||
--fix-only Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`
|
||||
--format <FORMAT> Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint]
|
||||
--config <CONFIG> Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
|
||||
--statistics Show counts for every rule with at least one violation
|
||||
--add-noqa Enable automatic additions of `noqa` directives to failing lines
|
||||
--show-files See the files Ruff will be run against with the current settings
|
||||
--show-settings See the settings Ruff will use to lint a given Python file
|
||||
-h, --help Print help
|
||||
--fix
|
||||
Attempt to automatically fix lint violations
|
||||
--show-source
|
||||
Show violations with source code
|
||||
--show-fixes
|
||||
Show an enumeration of all autofixed lint violations
|
||||
--diff
|
||||
Avoid writing any fixed files back; instead, output a diff for each changed file to stdout
|
||||
-w, --watch
|
||||
Run in watch mode by re-running whenever files change
|
||||
--fix-only
|
||||
Fix any fixable lint violations, but don't report on leftover violations. Implies `--fix`
|
||||
--format <FORMAT>
|
||||
Output serialization format for violations [env: RUFF_FORMAT=] [possible values: text, json, junit, grouped, github, gitlab, pylint]
|
||||
--target-version <TARGET_VERSION>
|
||||
The minimum Python version that should be supported
|
||||
--config <CONFIG>
|
||||
Path to the `pyproject.toml` or `ruff.toml` file to use for configuration
|
||||
--statistics
|
||||
Show counts for every rule with at least one violation
|
||||
--add-noqa
|
||||
Enable automatic additions of `noqa` directives to failing lines
|
||||
--show-files
|
||||
See the files Ruff will be run against with the current settings
|
||||
--show-settings
|
||||
See the settings Ruff will use to lint a given Python file
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
Rule selection:
|
||||
--select <RULE_CODE>
|
||||
@@ -456,14 +491,6 @@ File selection:
|
||||
--respect-gitignore Respect file exclusions via `.gitignore` and other standard ignore files
|
||||
--force-exclude Enforce exclusions, even for paths passed to Ruff directly on the command-line
|
||||
|
||||
Rule configuration:
|
||||
--target-version <TARGET_VERSION>
|
||||
The minimum Python version that should be supported
|
||||
--line-length <LINE_LENGTH>
|
||||
Set the line-length for length-associated rules and automatic formatting
|
||||
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
|
||||
Regular expression matching the name of dummy variables
|
||||
|
||||
Miscellaneous:
|
||||
-n, --no-cache
|
||||
Disable cache reads
|
||||
@@ -627,25 +654,44 @@ configuration.
|
||||
See the [`isort` documentation](https://pycqa.github.io/isort/docs/configuration/action_comments.html)
|
||||
for more.
|
||||
|
||||
#### Exit codes
|
||||
### Exit codes
|
||||
|
||||
By default, Ruff exits with the following status codes:
|
||||
|
||||
- `0` if no violations were found, or if all present violations were fixed automatically.
|
||||
- `1` if violations were found.
|
||||
- `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an internal error.
|
||||
* `0` if no violations were found, or if all present violations were fixed automatically.
|
||||
* `1` if violations were found.
|
||||
* `2` if Ruff terminates abnormally due to invalid configuration, invalid CLI options, or an internal error.
|
||||
|
||||
This convention mirrors that of tools like ESLint, Prettier, and RuboCop.
|
||||
|
||||
Ruff supports two command-line flags that alter its exit code behavior:
|
||||
|
||||
- `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found.
|
||||
* `--exit-zero` will cause Ruff to exit with a status code of `0` even if violations were found.
|
||||
Note that Ruff will still exit with a status code of `2` if it terminates abnormally.
|
||||
- `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were
|
||||
* `--exit-non-zero-on-fix` will cause Ruff to exit with a status code of `1` if violations were
|
||||
found, _even if_ all such violations were fixed automatically. Note that the use of
|
||||
`--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after
|
||||
autofixing.
|
||||
|
||||
### Autocompletion
|
||||
|
||||
Ruff supports autocompletion for most shells. A shell-specific completion script can be generated
|
||||
by `ruff generate-shell-completion <SHELL>`, where `<SHELL>` is one of `bash`, `elvish`, `fig`, `fish`,
|
||||
`powershell`, or `zsh`.
|
||||
|
||||
The exact steps required to enable autocompletion will vary by shell. For example instructions,
|
||||
see the [Poetry](https://python-poetry.org/docs/#enable-tab-completion-for-bash-fish-or-zsh) or
|
||||
[ripgrep](https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#complete) documentation.
|
||||
|
||||
As an example: to enable autocompletion for Zsh, run
|
||||
`ruff generate-shell-completion zsh > ~/.zfunc/_ruff`. Then add the following line to your
|
||||
`~/.zshrc` file, if they're not already present:
|
||||
|
||||
```zsh
|
||||
fpath+=~/.zfunc
|
||||
autoload -Uz compinit && compinit
|
||||
```
|
||||
|
||||
<!-- End section: Configuration -->
|
||||
|
||||
## Supported Rules
|
||||
@@ -671,7 +717,7 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| F401 | unused-import | `{name}` imported but unused; consider adding to `__all__` or using a redundant alias | 🛠 |
|
||||
| F402 | import-shadowed-by-loop-var | Import `{name}` from line {line} shadowed by loop variable | |
|
||||
| F403 | import-star-used | `from {name} import *` used; unable to detect undefined names | |
|
||||
| F403 | import-star | `from {name} import *` used; unable to detect undefined names | |
|
||||
| F404 | late-future-import | `from __future__` imports must occur at the beginning of the file | |
|
||||
| F405 | import-star-usage | `{name}` may be undefined, or defined from star imports: {sources} | |
|
||||
| F406 | import-star-not-permitted | `from {name} import *` only allowed at module level | |
|
||||
@@ -684,13 +730,13 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/) on PyPI.
|
||||
| F506 | percent-format-mixed-positional-and-named | `%`-format string has mixed positional and named placeholders | |
|
||||
| F507 | percent-format-positional-count-mismatch | `%`-format string has {wanted} placeholder(s) but {got} substitution(s) | |
|
||||
| F508 | percent-format-star-requires-sequence | `%`-format string `*` specifier requires sequence | |
|
||||
| F509 | percent-format-unsupported-format-character | `%`-format string has unsupported format character '{char}' | |
|
||||
| F509 | percent-format-unsupported-format-character | `%`-format string has unsupported format character `{char}` | |
|
||||
| F521 | string-dot-format-invalid-format | `.format` call has invalid format string: {message} | |
|
||||
| F522 | string-dot-format-extra-named-arguments | `.format` call has unused named argument(s): {message} | 🛠 |
|
||||
| F523 | string-dot-format-extra-positional-arguments | `.format` call has unused arguments at position(s): {message} | |
|
||||
| F524 | string-dot-format-missing-arguments | `.format` call is missing argument(s) for placeholder(s): {message} | |
|
||||
| F525 | string-dot-format-mixing-automatic | `.format` string mixes automatic and manual numbering | |
|
||||
| F541 | f-string-missing-placeholders | f-string without any placeholders | 🛠 |
|
||||
| F541 | [f-string-missing-placeholders](https://beta.ruff.rs/docs/rules/f-string-missing-placeholders/) | f-string without any placeholders | 🛠 |
|
||||
| F601 | multi-value-repeated-key-literal | Dictionary key literal `{name}` repeated | 🛠 |
|
||||
| F602 | multi-value-repeated-key-variable | Dictionary key `{name}` repeated | 🛠 |
|
||||
| F621 | expressions-in-star-assignment | Too many expressions in star-unpacking assignment | |
|
||||
@@ -709,7 +755,7 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/) on PyPI.
|
||||
| F821 | undefined-name | Undefined name `{name}` | |
|
||||
| F822 | undefined-export | Undefined name `{name}` in `__all__` | |
|
||||
| F823 | undefined-local | Local variable `{name}` referenced before assignment | |
|
||||
| F841 | unused-variable | Local variable `{name}` is assigned to but never used | 🛠 |
|
||||
| F841 | [unused-variable](https://beta.ruff.rs/docs/rules/unused-variable/) | Local variable `{name}` is assigned to but never used | 🛠 |
|
||||
| F842 | unused-annotation | Local variable `{name}` is annotated but never used | |
|
||||
| F901 | raise-not-implemented | `raise NotImplemented` should be `raise NotImplementedError` | 🛠 |
|
||||
|
||||
@@ -725,13 +771,16 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/) on PyPI.
|
||||
| E401 | multiple-imports-on-one-line | Multiple imports on one line | |
|
||||
| E402 | module-import-not-at-top-of-file | Module level import not at top of file | |
|
||||
| E501 | line-too-long | Line too long ({length} > {limit} characters) | |
|
||||
| E701 | multiple-statements-on-one-line-colon | Multiple statements on one line (colon) | |
|
||||
| E702 | multiple-statements-on-one-line-semicolon | Multiple statements on one line (semicolon) | |
|
||||
| E703 | useless-semicolon | Statement ends with an unnecessary semicolon | |
|
||||
| E711 | none-comparison | Comparison to `None` should be `cond is None` | 🛠 |
|
||||
| E712 | true-false-comparison | Comparison to `True` should be `cond is True` | 🛠 |
|
||||
| E713 | not-in-test | Test for membership should be `not in` | 🛠 |
|
||||
| E714 | not-is-test | Test for object identity should be `is not` | 🛠 |
|
||||
| E721 | type-comparison | Do not compare types, use `isinstance()` | |
|
||||
| E722 | do-not-use-bare-except | Do not use bare `except` | |
|
||||
| E731 | do-not-assign-lambda | Do not assign a `lambda` expression, use a `def` | 🛠 |
|
||||
| E722 | [bare-except](https://beta.ruff.rs/docs/rules/bare-except/) | Do not use bare `except` | |
|
||||
| E731 | lambda-assignment | Do not assign a `lambda` expression, use a `def` | 🛠 |
|
||||
| E741 | ambiguous-variable-name | Ambiguous variable name: `{name}` | |
|
||||
| E742 | ambiguous-class-name | Ambiguous class name: `{name}` | |
|
||||
| E743 | ambiguous-function-name | Ambiguous function name: `{name}` | |
|
||||
@@ -744,7 +793,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| W292 | no-new-line-at-end-of-file | No newline at end of file | 🛠 |
|
||||
| W505 | doc-line-too-long | Doc line too long ({length} > {limit} characters) | |
|
||||
| W605 | invalid-escape-sequence | Invalid escape sequence: '\{char}' | 🛠 |
|
||||
| W605 | invalid-escape-sequence | Invalid escape sequence: `\{char}` | 🛠 |
|
||||
|
||||
### mccabe (C90)
|
||||
|
||||
@@ -752,7 +801,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| C901 | function-is-too-complex | `{name}` is too complex ({complexity}) | |
|
||||
| C901 | [complex-structure](https://beta.ruff.rs/docs/rules/complex-structure/) | `{name}` is too complex ({complexity}) | |
|
||||
|
||||
### isort (I)
|
||||
|
||||
@@ -760,8 +809,8 @@ For more, see [isort](https://pypi.org/project/isort/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| I001 | unsorted-imports | Import block is un-sorted or un-formatted | 🛠 |
|
||||
| I002 | missing-required-import | Missing required import: `{name}` | 🛠 |
|
||||
| I001 | [unsorted-imports](https://beta.ruff.rs/docs/rules/unsorted-imports/) | Import block is un-sorted or un-formatted | 🛠 |
|
||||
| I002 | [missing-required-import](https://beta.ruff.rs/docs/rules/missing-required-import/) | Missing required import: `{name}` | 🛠 |
|
||||
|
||||
### pep8-naming (N)
|
||||
|
||||
@@ -815,13 +864,13 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/) on PyPI.
|
||||
| D213 | multi-line-summary-second-line | Multi-line docstring summary should start at the second line | 🛠 |
|
||||
| D214 | section-not-over-indented | Section is over-indented ("{name}") | 🛠 |
|
||||
| D215 | section-underline-not-over-indented | Section underline is over-indented ("{name}") | 🛠 |
|
||||
| D300 | uses-triple-quotes | Use """triple double quotes""" | |
|
||||
| D301 | uses-r-prefix-for-backslashed-content | Use r""" if any backslashes in a docstring | |
|
||||
| D300 | triple-single-quotes | Use triple double quotes `"""` | |
|
||||
| D301 | escape-sequence-in-docstring | Use `r"""` if any backslashes in a docstring | |
|
||||
| D400 | ends-in-period | First line should end with a period | 🛠 |
|
||||
| D401 | non-imperative-mood | First line of docstring should be in imperative mood: "{first_line}" | |
|
||||
| D402 | no-signature | First line should not be the function's signature | |
|
||||
| D403 | first-line-capitalized | First word of the first line should be properly capitalized | |
|
||||
| D404 | no-this-prefix | First word of the docstring should not be "This" | |
|
||||
| D404 | docstring-starts-with-this | First word of the docstring should not be "This" | |
|
||||
| D405 | capitalize-section-name | Section name should be properly capitalized ("{name}") | 🛠 |
|
||||
| D406 | new-line-after-section-name | Section name should end with a newline ("{name}") | 🛠 |
|
||||
| D407 | dashed-underline-after-section | Missing dashed underline after section ("{name}") | 🛠 |
|
||||
@@ -831,12 +880,12 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/) on PyPI.
|
||||
| D411 | blank-line-before-section | Missing blank line before section ("{name}") | 🛠 |
|
||||
| D412 | no-blank-lines-between-header-and-content | No blank lines allowed between a section header and its content ("{name}") | 🛠 |
|
||||
| D413 | blank-line-after-last-section | Missing blank line after last section ("{name}") | 🛠 |
|
||||
| D414 | non-empty-section | Section has no content ("{name}") | |
|
||||
| D414 | empty-docstring-section | Section has no content ("{name}") | |
|
||||
| D415 | ends-in-punctuation | First line should end with a period, question mark, or exclamation point | 🛠 |
|
||||
| D416 | section-name-ends-in-colon | Section name should end with a colon ("{name}") | 🛠 |
|
||||
| D417 | document-all-arguments | Missing argument description in the docstring: `{name}` | |
|
||||
| D418 | skip-docstring | Function decorated with `@overload` shouldn't contain a docstring | |
|
||||
| D419 | non-empty | Docstring is empty | |
|
||||
| D417 | undocumented-param | Missing argument description in the docstring: `{name}` | |
|
||||
| D418 | overload-with-docstring | Function decorated with `@overload` shouldn't contain a docstring | |
|
||||
| D419 | empty-docstring | Docstring is empty | |
|
||||
|
||||
### pyupgrade (UP)
|
||||
|
||||
@@ -848,10 +897,10 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/) on PyPI.
|
||||
| UP003 | type-of-primitive | Use `{}` instead of `type(...)` | 🛠 |
|
||||
| UP004 | useless-object-inheritance | Class `{name}` inherits from `object` | 🛠 |
|
||||
| UP005 | deprecated-unittest-alias | `{alias}` is deprecated, use `{target}` | 🛠 |
|
||||
| UP006 | use-pep585-annotation | Use `{}` instead of `{}` for type annotations | 🛠 |
|
||||
| UP007 | use-pep604-annotation | Use `X \| Y` for type annotations | 🛠 |
|
||||
| UP006 | deprecated-collection-type | Use `{}` instead of `{}` for type annotations | 🛠 |
|
||||
| UP007 | typing-union | Use `X \| Y` for type annotations | 🛠 |
|
||||
| UP008 | super-call-with-parameters | Use `super()` instead of `super(__class__, self)` | 🛠 |
|
||||
| UP009 | pep3120-unnecessary-coding-comment | UTF-8 encoding declaration is unnecessary | 🛠 |
|
||||
| UP009 | utf8-encoding-declaration | UTF-8 encoding declaration is unnecessary | 🛠 |
|
||||
| UP010 | unnecessary-future-import | Unnecessary `__future__` import `{import}` for target Python version | 🛠 |
|
||||
| UP011 | lru-cache-without-parameters | Unnecessary parameters to `functools.lru_cache` | 🛠 |
|
||||
| UP012 | unnecessary-encode-utf8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
|
||||
@@ -903,17 +952,17 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ANN001 | missing-type-function-argument | Missing type annotation for function argument `{name}` | |
|
||||
| ANN002 | missing-type-args | Missing type annotation for `*{name}` | |
|
||||
| ANN003 | missing-type-kwargs | Missing type annotation for `**{name}` | |
|
||||
| ANN101 | missing-type-self | Missing type annotation for `{name}` in method | |
|
||||
| ANN102 | missing-type-cls | Missing type annotation for `{name}` in classmethod | |
|
||||
| ANN201 | missing-return-type-public-function | Missing return type annotation for public function `{name}` | |
|
||||
| ANN202 | missing-return-type-private-function | Missing return type annotation for private function `{name}` | |
|
||||
| ANN204 | missing-return-type-special-method | Missing return type annotation for special method `{name}` | 🛠 |
|
||||
| ANN205 | missing-return-type-static-method | Missing return type annotation for staticmethod `{name}` | |
|
||||
| ANN206 | missing-return-type-class-method | Missing return type annotation for classmethod `{name}` | |
|
||||
| ANN401 | dynamically-typed-expression | Dynamically typed expressions (typing.Any) are disallowed in `{name}` | |
|
||||
| ANN001 | [missing-type-function-argument](https://beta.ruff.rs/docs/rules/missing-type-function-argument/) | Missing type annotation for function argument `{name}` | |
|
||||
| ANN002 | [missing-type-args](https://beta.ruff.rs/docs/rules/missing-type-args/) | Missing type annotation for `*{name}` | |
|
||||
| ANN003 | [missing-type-kwargs](https://beta.ruff.rs/docs/rules/missing-type-kwargs/) | Missing type annotation for `**{name}` | |
|
||||
| ANN101 | [missing-type-self](https://beta.ruff.rs/docs/rules/missing-type-self/) | Missing type annotation for `{name}` in method | |
|
||||
| ANN102 | [missing-type-cls](https://beta.ruff.rs/docs/rules/missing-type-cls/) | Missing type annotation for `{name}` in classmethod | |
|
||||
| ANN201 | [missing-return-type-public-function](https://beta.ruff.rs/docs/rules/missing-return-type-public-function/) | Missing return type annotation for public function `{name}` | |
|
||||
| ANN202 | [missing-return-type-private-function](https://beta.ruff.rs/docs/rules/missing-return-type-private-function/) | Missing return type annotation for private function `{name}` | |
|
||||
| ANN204 | [missing-return-type-special-method](https://beta.ruff.rs/docs/rules/missing-return-type-special-method/) | Missing return type annotation for special method `{name}` | 🛠 |
|
||||
| ANN205 | [missing-return-type-static-method](https://beta.ruff.rs/docs/rules/missing-return-type-static-method/) | Missing return type annotation for staticmethod `{name}` | |
|
||||
| ANN206 | [missing-return-type-class-method](https://beta.ruff.rs/docs/rules/missing-return-type-class-method/) | Missing return type annotation for classmethod `{name}` | |
|
||||
| ANN401 | [any-type](https://beta.ruff.rs/docs/rules/any-type/) | Dynamically typed expressions (typing.Any) are disallowed in `{name}` | |
|
||||
|
||||
### flake8-bandit (S)
|
||||
|
||||
@@ -921,8 +970,8 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| S101 | assert-used | Use of `assert` detected | |
|
||||
| S102 | exec-used | Use of `exec` detected | |
|
||||
| S101 | assert | Use of `assert` detected | |
|
||||
| S102 | exec-builtin | Use of `exec` detected | |
|
||||
| S103 | bad-file-permissions | `os.chmod` setting a permissive mask `{mask:#o}` on file or directory | |
|
||||
| S104 | hardcoded-bind-all-interfaces | Possible binding to all interfaces | |
|
||||
| S105 | hardcoded-password-string | Possible hardcoded password: "{}" | |
|
||||
@@ -930,12 +979,14 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/) on PyPI.
|
||||
| S107 | hardcoded-password-default | Possible hardcoded password: "{}" | |
|
||||
| S108 | hardcoded-temp-file | Probable insecure usage of temporary file or directory: "{}" | |
|
||||
| S110 | try-except-pass | `try`-`except`-`pass` detected, consider logging the exception | |
|
||||
| S112 | try-except-continue | `try`-`except`-`continue` detected, consider logging the exception | |
|
||||
| S113 | request-without-timeout | Probable use of requests call with timeout set to `{value}` | |
|
||||
| S324 | hashlib-insecure-hash-function | Probable use of insecure hash functions in `hashlib`: "{}" | |
|
||||
| S324 | hashlib-insecure-hash-function | Probable use of insecure hash functions in `hashlib`: `{string}` | |
|
||||
| S501 | request-with-no-cert-validation | Probable use of `{string}` call with `verify=False` disabling SSL certificate checks | |
|
||||
| S506 | unsafe-yaml-load | Probable use of unsafe loader `{name}` with `yaml.load`. Allows instantiation of arbitrary objects. Consider `yaml.safe_load`. | |
|
||||
| S508 | snmp-insecure-version | The use of SNMPv1 and SNMPv2 is insecure. Use SNMPv3 if able. | |
|
||||
| S509 | snmp-weak-cryptography | You should not use SNMPv3 without encryption. `noAuthNoPriv` & `authNoPriv` is insecure. | |
|
||||
| S608 | [hardcoded-sql-expression](https://beta.ruff.rs/docs/rules/hardcoded-sql-expression/) | Possible SQL injection vector through string-based query construction | |
|
||||
| S612 | logging-config-insecure-listen | Use of insecure `logging.config.listen` detected | |
|
||||
| S701 | jinja2-autoescape-false | Using jinja2 templates with `autoescape=False` is dangerous and can lead to XSS. Ensure `autoescape=True` or use the `select_autoescape` function. | |
|
||||
|
||||
@@ -972,13 +1023,13 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/) on PyPI
|
||||
| B008 | function-call-argument-default | Do not perform function call `{name}` in argument defaults | |
|
||||
| B009 | get-attr-with-constant | Do not call `getattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
|
||||
| B010 | set-attr-with-constant | Do not call `setattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
|
||||
| B011 | do-not-assert-false | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
|
||||
| B011 | assert-false | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
|
||||
| B012 | jump-statement-in-finally | `{name}` inside `finally` blocks cause exceptions to be silenced | |
|
||||
| B013 | redundant-tuple-in-exception-handler | A length-one tuple literal is redundant. Write `except {name}` instead of `except ({name},)`. | 🛠 |
|
||||
| B014 | duplicate-handler-exception | Exception handler with duplicate exception: `{name}` | 🛠 |
|
||||
| B015 | useless-comparison | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. | |
|
||||
| B016 | cannot-raise-literal | Cannot raise a literal. Did you intend to return it or raise an Exception? | |
|
||||
| [B017](https://github.com/charliermarsh/ruff/blob/main/docs/rules/assert-raises-exception.md) | [assert-raises-exception](https://github.com/charliermarsh/ruff/blob/main/docs/rules/assert-raises-exception.md) | `assertRaises(Exception)` should be considered evil | |
|
||||
| B017 | [assert-raises-exception](https://beta.ruff.rs/docs/rules/assert-raises-exception/) | `assertRaises(Exception)` should be considered evil | |
|
||||
| B018 | useless-expression | Found useless expression. Either assign it to a variable or remove it. | |
|
||||
| B019 | cached-instance-method | Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | |
|
||||
| B020 | loop-variable-overrides-iterator | Loop control variable `{name}` overrides iterable it iterates | |
|
||||
@@ -998,9 +1049,9 @@ For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/) on Py
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| A001 | builtin-variable-shadowing | Variable `{name}` is shadowing a python builtin | |
|
||||
| A002 | builtin-argument-shadowing | Argument `{name}` is shadowing a python builtin | |
|
||||
| A003 | builtin-attribute-shadowing | Class attribute `{name}` is shadowing a python builtin | |
|
||||
| A001 | [builtin-variable-shadowing](https://beta.ruff.rs/docs/rules/builtin-variable-shadowing/) | Variable `{name}` is shadowing a python builtin | |
|
||||
| A002 | [builtin-argument-shadowing](https://beta.ruff.rs/docs/rules/builtin-argument-shadowing/) | Argument `{name}` is shadowing a python builtin | |
|
||||
| A003 | [builtin-attribute-shadowing](https://beta.ruff.rs/docs/rules/builtin-attribute-shadowing/) | Class attribute `{name}` is shadowing a python builtin | |
|
||||
|
||||
### flake8-commas (COM)
|
||||
|
||||
@@ -1018,9 +1069,9 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| C400 | unnecessary-generator-list | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
|
||||
| C401 | unnecessary-generator-set | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
|
||||
| C402 | unnecessary-generator-dict | Unnecessary generator (rewrite as a `dict` comprehension) | 🛠 |
|
||||
| C400 | [unnecessary-generator-list](https://beta.ruff.rs/docs/rules/unnecessary-generator-list/) | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
|
||||
| C401 | [unnecessary-generator-set](https://beta.ruff.rs/docs/rules/unnecessary-generator-set/) | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
|
||||
| C402 | [unnecessary-generator-dict](https://beta.ruff.rs/docs/rules/unnecessary-generator-dict/) | Unnecessary generator (rewrite as a `dict` comprehension) | 🛠 |
|
||||
| C403 | unnecessary-list-comprehension-set | Unnecessary `list` comprehension (rewrite as a `set` comprehension) | 🛠 |
|
||||
| C404 | unnecessary-list-comprehension-dict | Unnecessary `list` comprehension (rewrite as a `dict` comprehension) | 🛠 |
|
||||
| C405 | unnecessary-literal-set | Unnecessary `{obj_type}` literal (rewrite as a `set` literal) | 🛠 |
|
||||
@@ -1029,11 +1080,11 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
| C409 | unnecessary-literal-within-tuple-call | Unnecessary `{literal}` literal passed to `tuple()` (rewrite as a `tuple` literal) | 🛠 |
|
||||
| C410 | unnecessary-literal-within-list-call | Unnecessary `{literal}` literal passed to `list()` (remove the outer call to `list()`) | 🛠 |
|
||||
| C411 | unnecessary-list-call | Unnecessary `list` call (remove the outer call to `list()`) | 🛠 |
|
||||
| C413 | unnecessary-call-around-sorted | Unnecessary `{func}` call around `sorted()` | 🛠 |
|
||||
| C414 | unnecessary-double-cast-or-process | Unnecessary `{inner}` call within `{outer}()` | |
|
||||
| C413 | [unnecessary-call-around-sorted](https://beta.ruff.rs/docs/rules/unnecessary-call-around-sorted/) | Unnecessary `{func}` call around `sorted()` | 🛠 |
|
||||
| C414 | [unnecessary-double-cast-or-process](https://beta.ruff.rs/docs/rules/unnecessary-double-cast-or-process/) | Unnecessary `{inner}` call within `{outer}()` | 🛠 |
|
||||
| C415 | unnecessary-subscript-reversal | Unnecessary subscript reversal of iterable within `{func}()` | |
|
||||
| C416 | unnecessary-comprehension | Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`) | 🛠 |
|
||||
| C417 | unnecessary-map | Unnecessary `map` usage (rewrite using a generator expression) | |
|
||||
| C417 | [unnecessary-map](https://beta.ruff.rs/docs/rules/unnecessary-map/) | Unnecessary `map` usage (rewrite using a generator expression) | 🛠 |
|
||||
|
||||
### flake8-datetimez (DTZ)
|
||||
|
||||
@@ -1059,15 +1110,25 @@ For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/) on Py
|
||||
| ---- | ---- | ------- | --- |
|
||||
| T100 | debugger | Trace found: `{name}` used | |
|
||||
|
||||
### flake8-django (DJ)
|
||||
|
||||
For more, see [flake8-django](https://pypi.org/project/flake8-django/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| DJ001 | [nullable-model-string-field](https://beta.ruff.rs/docs/rules/nullable-model-string-field/) | Avoid using `null=True` on string-based fields such as {field_name} | |
|
||||
| DJ008 | [model-without-dunder-str](https://beta.ruff.rs/docs/rules/model-without-dunder-str/) | Model does not define `__str__` method | |
|
||||
| DJ013 | [non-leading-receiver-decorator](https://beta.ruff.rs/docs/rules/non-leading-receiver-decorator/) | `@receiver` decorator must be on top of all the other decorators | |
|
||||
|
||||
### flake8-errmsg (EM)
|
||||
|
||||
For more, see [flake8-errmsg](https://pypi.org/project/flake8-errmsg/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| EM101 | raw-string-in-exception | Exception must not use a string literal, assign to variable first | |
|
||||
| EM102 | f-string-in-exception | Exception must not use an f-string literal, assign to variable first | |
|
||||
| EM103 | dot-format-in-exception | Exception must not use a `.format()` string directly, assign to variable first | |
|
||||
| EM101 | [raw-string-in-exception](https://beta.ruff.rs/docs/rules/raw-string-in-exception/) | Exception must not use a string literal, assign to variable first | |
|
||||
| EM102 | [f-string-in-exception](https://beta.ruff.rs/docs/rules/f-string-in-exception/) | Exception must not use an f-string literal, assign to variable first | |
|
||||
| EM103 | [dot-format-in-exception](https://beta.ruff.rs/docs/rules/dot-format-in-exception/) | Exception must not use a `.format()` string directly, assign to variable first | |
|
||||
|
||||
### flake8-executable (EXE)
|
||||
|
||||
@@ -1077,7 +1138,7 @@ For more, see [flake8-executable](https://pypi.org/project/flake8-executable/) o
|
||||
| ---- | ---- | ------- | --- |
|
||||
| EXE001 | shebang-not-executable | Shebang is present but file is not executable | |
|
||||
| EXE002 | shebang-missing-executable-file | The file is executable but no shebang is present | |
|
||||
| EXE003 | shebang-python | Shebang should contain "python" | |
|
||||
| EXE003 | shebang-python | Shebang should contain `python` | |
|
||||
| EXE004 | shebang-whitespace | Avoid whitespace before shebang | 🛠 |
|
||||
| EXE005 | shebang-newline | Shebang should be at the beginning of the file | |
|
||||
|
||||
@@ -1097,7 +1158,7 @@ For more, see [flake8-import-conventions](https://github.com/joaopalmeiro/flake8
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| [ICN001](https://github.com/charliermarsh/ruff/blob/main/docs/rules/unconventional-import-alias.md) | [unconventional-import-alias](https://github.com/charliermarsh/ruff/blob/main/docs/rules/unconventional-import-alias.md) | `{name}` should be imported as `{asname}` | |
|
||||
| ICN001 | [unconventional-import-alias](https://beta.ruff.rs/docs/rules/unconventional-import-alias/) | `{name}` should be imported as `{asname}` | |
|
||||
|
||||
### flake8-logging-format (G)
|
||||
|
||||
@@ -1120,7 +1181,7 @@ For more, see [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420/) on
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| [INP001](https://github.com/charliermarsh/ruff/blob/main/docs/rules/implicit-namespace-package.md) | [implicit-namespace-package](https://github.com/charliermarsh/ruff/blob/main/docs/rules/implicit-namespace-package.md) | File `{filename}` is part of an implicit namespace package. Add an `__init__.py`. | |
|
||||
| INP001 | [implicit-namespace-package](https://beta.ruff.rs/docs/rules/implicit-namespace-package/) | File `{filename}` is part of an implicit namespace package. Add an `__init__.py`. | |
|
||||
|
||||
### flake8-pie (PIE)
|
||||
|
||||
@@ -1128,11 +1189,11 @@ For more, see [flake8-pie](https://pypi.org/project/flake8-pie/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PIE790 | no-unnecessary-pass | Unnecessary `pass` statement | 🛠 |
|
||||
| PIE790 | unnecessary-pass | Unnecessary `pass` statement | 🛠 |
|
||||
| PIE794 | dupe-class-field-definitions | Class field `{name}` is defined multiple times | 🛠 |
|
||||
| PIE796 | prefer-unique-enums | Enum contains duplicate value: `{value}` | |
|
||||
| PIE800 | no-unnecessary-spread | Unnecessary spread `**` | |
|
||||
| PIE804 | no-unnecessary-dict-kwargs | Unnecessary `dict` kwargs | |
|
||||
| PIE800 | unnecessary-spread | Unnecessary spread `**` | |
|
||||
| PIE804 | unnecessary-dict-kwargs | Unnecessary `dict` kwargs | |
|
||||
| PIE807 | prefer-list-builtin | Prefer `list` over useless lambda | 🛠 |
|
||||
| PIE810 | single-starts-ends-with | Call `{attr}` once with a `tuple` | |
|
||||
|
||||
@@ -1145,6 +1206,16 @@ For more, see [flake8-print](https://pypi.org/project/flake8-print/) on PyPI.
|
||||
| T201 | print-found | `print` found | |
|
||||
| T203 | p-print-found | `pprint` found | |
|
||||
|
||||
### flake8-pyi (PYI)
|
||||
|
||||
For more, see [flake8-pyi](https://pypi.org/project/flake8-pyi/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PYI001 | [prefix-type-params](https://beta.ruff.rs/docs/rules/prefix-type-params/) | Name of private `{kind}` must start with `_` | |
|
||||
| PYI007 | [unrecognized-platform-check](https://beta.ruff.rs/docs/rules/unrecognized-platform-check/) | Unrecognized `sys.platform` check | |
|
||||
| PYI008 | [unrecognized-platform-name](https://beta.ruff.rs/docs/rules/unrecognized-platform-name/) | Unrecognized platform `{platform}` | |
|
||||
|
||||
### flake8-pytest-style (PT)
|
||||
|
||||
For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/) on PyPI.
|
||||
@@ -1183,10 +1254,18 @@ For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| [Q000](https://github.com/charliermarsh/ruff/blob/main/docs/rules/bad-quotes-inline-string.md) | [bad-quotes-inline-string](https://github.com/charliermarsh/ruff/blob/main/docs/rules/bad-quotes-inline-string.md) | Double quotes found but single quotes preferred | 🛠 |
|
||||
| [Q001](https://github.com/charliermarsh/ruff/blob/main/docs/rules/bad-quotes-multiline-string.md) | [bad-quotes-multiline-string](https://github.com/charliermarsh/ruff/blob/main/docs/rules/bad-quotes-multiline-string.md) | Double quote multiline found but single quotes preferred | 🛠 |
|
||||
| [Q002](https://github.com/charliermarsh/ruff/blob/main/docs/rules/bad-quotes-docstring.md) | [bad-quotes-docstring](https://github.com/charliermarsh/ruff/blob/main/docs/rules/bad-quotes-docstring.md) | Double quote docstring found but single quotes preferred | 🛠 |
|
||||
| [Q003](https://github.com/charliermarsh/ruff/blob/main/docs/rules/avoid-quote-escape.md) | [avoid-quote-escape](https://github.com/charliermarsh/ruff/blob/main/docs/rules/avoid-quote-escape.md) | Change outer quotes to avoid escaping inner quotes | 🛠 |
|
||||
| Q000 | [bad-quotes-inline-string](https://beta.ruff.rs/docs/rules/bad-quotes-inline-string/) | Double quotes found but single quotes preferred | 🛠 |
|
||||
| Q001 | [bad-quotes-multiline-string](https://beta.ruff.rs/docs/rules/bad-quotes-multiline-string/) | Double quote multiline found but single quotes preferred | 🛠 |
|
||||
| Q002 | [bad-quotes-docstring](https://beta.ruff.rs/docs/rules/bad-quotes-docstring/) | Double quote docstring found but single quotes preferred | 🛠 |
|
||||
| Q003 | [avoidable-escaped-quote](https://beta.ruff.rs/docs/rules/avoidable-escaped-quote/) | Change outer quotes to avoid escaping inner quotes | 🛠 |
|
||||
|
||||
### flake8-raise (RSE)
|
||||
|
||||
For more, see [flake8-raise](https://pypi.org/project/flake8-raise/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| RSE102 | unnecessary-paren-on-raise-exception | Unnecessary parentheses on raised exception | 🛠 |
|
||||
|
||||
### flake8-return (RET)
|
||||
|
||||
@@ -1203,6 +1282,14 @@ For more, see [flake8-return](https://pypi.org/project/flake8-return/) on PyPI.
|
||||
| RET507 | superfluous-else-continue | Unnecessary `{branch}` after `continue` statement | |
|
||||
| RET508 | superfluous-else-break | Unnecessary `{branch}` after `break` statement | |
|
||||
|
||||
### flake8-self (SLF)
|
||||
|
||||
For more, see [flake8-self](https://pypi.org/project/flake8-self/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| SLF001 | [private-member-access](https://beta.ruff.rs/docs/rules/private-member-access/) | Private member accessed: `{access}` | |
|
||||
|
||||
### flake8-simplify (SIM)
|
||||
|
||||
For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/) on PyPI.
|
||||
@@ -1210,8 +1297,8 @@ For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/) on Py
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| SIM101 | duplicate-isinstance-call | Multiple `isinstance` calls for `{name}`, merge into a single call | 🛠 |
|
||||
| SIM102 | nested-if-statements | Use a single `if` statement instead of nested `if` statements | 🛠 |
|
||||
| SIM103 | return-bool-condition-directly | Return the condition `{cond}` directly | 🛠 |
|
||||
| SIM102 | collapsible-if | Use a single `if` statement instead of nested `if` statements | 🛠 |
|
||||
| SIM103 | needless-bool | Return the condition `{condition}` directly | 🛠 |
|
||||
| SIM105 | use-contextlib-suppress | Use `contextlib.suppress({exception})` instead of try-except-pass | |
|
||||
| SIM107 | return-in-try-except-finally | Don't use `return` in `try`/`except` and `finally` | |
|
||||
| SIM108 | use-ternary-operator | Use ternary operator `{contents}` instead of if-else-block | 🛠 |
|
||||
@@ -1219,6 +1306,7 @@ For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/) on Py
|
||||
| SIM110 | convert-loop-to-any | Use `{any}` instead of `for` loop | 🛠 |
|
||||
| SIM111 | convert-loop-to-all | Use `{all}` instead of `for` loop | 🛠 |
|
||||
| SIM112 | use-capital-environment-variables | Use capitalized environment variable `{expected}` instead of `{original}` | 🛠 |
|
||||
| SIM114 | [if-with-same-arms](https://beta.ruff.rs/docs/rules/if-with-same-arms/) | Combine `if` branches using logical `or` operator | |
|
||||
| SIM115 | open-file-with-context-handler | Use context handler for opening files | |
|
||||
| SIM117 | multiple-with-statements | Use a single `with` statement with multiple contexts instead of nested `with` statements | 🛠 |
|
||||
| SIM118 | key-in-dict | Use `{key} in {dict}` instead of `{key} in {dict}.keys()` | 🛠 |
|
||||
@@ -1241,8 +1329,8 @@ For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| TID251 | banned-api | `{name}` is banned: {message} | |
|
||||
| TID252 | relative-imports | Relative imports from parent modules are banned | |
|
||||
| TID251 | [banned-api](https://beta.ruff.rs/docs/rules/banned-api/) | `{name}` is banned: {message} | |
|
||||
| TID252 | [relative-imports](https://beta.ruff.rs/docs/rules/relative-imports/) | Relative imports from parent modules are banned | 🛠 |
|
||||
|
||||
### flake8-type-checking (TCH)
|
||||
|
||||
@@ -1306,7 +1394,7 @@ For more, see [eradicate](https://pypi.org/project/eradicate/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| [ERA001](https://github.com/charliermarsh/ruff/blob/main/docs/rules/commented-out-code.md) | [commented-out-code](https://github.com/charliermarsh/ruff/blob/main/docs/rules/commented-out-code.md) | Found commented-out code | 🛠 |
|
||||
| ERA001 | [commented-out-code](https://beta.ruff.rs/docs/rules/commented-out-code/) | Found commented-out code | 🛠 |
|
||||
|
||||
### pandas-vet (PD)
|
||||
|
||||
@@ -1314,7 +1402,7 @@ For more, see [pandas-vet](https://pypi.org/project/pandas-vet/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PD002 | use-of-inplace-argument | `inplace=True` should be avoided; it has inconsistent behavior | 🛠 |
|
||||
| PD002 | [use-of-inplace-argument](https://beta.ruff.rs/docs/rules/use-of-inplace-argument/) | `inplace=True` should be avoided; it has inconsistent behavior | 🛠 |
|
||||
| PD003 | use-of-dot-is-null | `.isna` is preferred to `.isnull`; functionality is equivalent | |
|
||||
| PD004 | use-of-dot-not-null | `.notna` is preferred to `.notnull`; functionality is equivalent | |
|
||||
| PD007 | use-of-dot-ix | `.ix` is deprecated; use more explicit `.loc` or `.iloc` | |
|
||||
@@ -1353,13 +1441,15 @@ For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PLE0100 | [yield-in-init](https://beta.ruff.rs/docs/rules/yield-in-init/) | `__init__` method is a generator | |
|
||||
| PLE0117 | nonlocal-without-binding | Nonlocal name `{name}` found without binding | |
|
||||
| PLE0118 | used-prior-global-declaration | Name `{name}` is used prior to global declaration on line {line} | |
|
||||
| PLE0604 | invalid-all-object | Invalid object in `__all__`, must contain only strings | |
|
||||
| PLE0605 | invalid-all-format | Invalid format for `__all__`, must be `tuple` or `list` | |
|
||||
| PLE1142 | await-outside-async | `await` should be used within an async function | |
|
||||
| PLE1307 | [bad-string-format-type](https://beta.ruff.rs/docs/rules/bad-string-format-type/) | Format type does not match argument type | |
|
||||
| PLE1310 | bad-str-strip-call | String `{strip}` call contains duplicate characters (did you mean `{removal}`?) | |
|
||||
| PLE2502 | bidirectional-unicode | Avoid using bidirectional unicode | |
|
||||
| PLE2502 | bidirectional-unicode | Contains control characters that can permit obfuscated code | |
|
||||
|
||||
#### Refactor (PLR)
|
||||
|
||||
@@ -1380,7 +1470,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PLW0120 | useless-else-on-loop | Else clause on loop without a break statement, remove the else and de-indent all the code inside it | |
|
||||
| PLW0120 | useless-else-on-loop | `else` clause on loop without a `break` statement; remove the `else` and de-indent all the code inside it | |
|
||||
| PLW0602 | global-variable-not-assigned | Using global for `{name}` but no assignment is done | |
|
||||
|
||||
### tryceratops (TRY)
|
||||
@@ -1389,41 +1479,32 @@ For more, see [tryceratops](https://pypi.org/project/tryceratops/1.1.0/) on PyPI
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| [TRY002](https://github.com/charliermarsh/ruff/blob/main/docs/rules/raise-vanilla-class.md) | [raise-vanilla-class](https://github.com/charliermarsh/ruff/blob/main/docs/rules/raise-vanilla-class.md) | Create your own exception | |
|
||||
| TRY002 | [raise-vanilla-class](https://beta.ruff.rs/docs/rules/raise-vanilla-class/) | Create your own exception | |
|
||||
| TRY003 | raise-vanilla-args | Avoid specifying long messages outside the exception class | |
|
||||
| TRY004 | prefer-type-error | Prefer `TypeError` exception for invalid type | 🛠 |
|
||||
| TRY004 | prefer-type-error | Prefer `TypeError` exception for invalid type | |
|
||||
| TRY200 | reraise-no-cause | Use `raise from` to specify exception cause | |
|
||||
| TRY201 | verbose-raise | Use `raise` without specifying exception name | |
|
||||
| TRY300 | try-consider-else | Consider moving this statement to an `else` block | |
|
||||
| TRY301 | raise-within-try | Abstract `raise` to an inner function | |
|
||||
| TRY400 | error-instead-of-exception | Use `logging.exception` instead of `logging.error` | |
|
||||
|
||||
### flake8-raise (RSE)
|
||||
|
||||
For more, see [flake8-raise](https://pypi.org/project/flake8-raise/) on PyPI.
|
||||
### NumPy-specific rules (NPY)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| RSE102 | unnecessary-paren-on-raise-exception | Unnecessary parentheses on raised exception | 🛠 |
|
||||
|
||||
### flake8-self (SLF)
|
||||
|
||||
For more, see [flake8-self](https://pypi.org/project/flake8-self/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| SLF001 | private-member-access | Private member accessed: `{access}` | |
|
||||
| NPY001 | [numpy-deprecated-type-alias](https://beta.ruff.rs/docs/rules/numpy-deprecated-type-alias/) | Type alias `np.{type_name}` is deprecated, replace with builtin type | 🛠 |
|
||||
|
||||
### Ruff-specific rules (RUF)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| RUF001 | ambiguous-unicode-character-string | String contains ambiguous unicode character '{confusable}' (did you mean '{representant}'?) | 🛠 |
|
||||
| RUF002 | ambiguous-unicode-character-docstring | Docstring contains ambiguous unicode character '{confusable}' (did you mean '{representant}'?) | 🛠 |
|
||||
| RUF003 | ambiguous-unicode-character-comment | Comment contains ambiguous unicode character '{confusable}' (did you mean '{representant}'?) | 🛠 |
|
||||
| RUF001 | ambiguous-unicode-character-string | String contains ambiguous unicode character `{confusable}` (did you mean `{representant}`?) | 🛠 |
|
||||
| RUF002 | ambiguous-unicode-character-docstring | Docstring contains ambiguous unicode character `{confusable}` (did you mean `{representant}`?) | 🛠 |
|
||||
| RUF003 | ambiguous-unicode-character-comment | Comment contains ambiguous unicode character `{confusable}` (did you mean `{representant}`?) | 🛠 |
|
||||
| RUF004 | keyword-argument-before-star-argument | Keyword argument `{name}` must come after starred arguments | |
|
||||
| RUF005 | unpack-instead-of-concatenating-to-collection-literal | Consider `{expr}` instead of concatenation | |
|
||||
| RUF100 | unused-noqa | Unused blanket `noqa` directive | 🛠 |
|
||||
| RUF005 | unpack-instead-of-concatenating-to-collection-literal | Consider `{expr}` instead of concatenation | 🛠 |
|
||||
| RUF006 | [asyncio-dangling-task](https://beta.ruff.rs/docs/rules/asyncio-dangling-task/) | Store a reference to the return value of `asyncio.create_task` | |
|
||||
| RUF100 | unused-noqa | Unused `noqa` directive | 🛠 |
|
||||
|
||||
<!-- End auto-generated sections. -->
|
||||
|
||||
@@ -1694,6 +1775,7 @@ natively, including:
|
||||
* [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
* [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
* [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
* [flake8-django](https://pypi.org/project/flake8-django/) ([#2817](https://github.com/charliermarsh/ruff/issues/2817))
|
||||
* [flake8-docstrings](https://pypi.org/project/flake8-docstrings/)
|
||||
* [flake8-eradicate](https://pypi.org/project/flake8-eradicate/)
|
||||
* [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
@@ -1704,6 +1786,7 @@ natively, including:
|
||||
* [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420)
|
||||
* [flake8-pie](https://pypi.org/project/flake8-pie/)
|
||||
* [flake8-print](https://pypi.org/project/flake8-print/)
|
||||
* [flake8-pyi](https://pypi.org/project/flake8-pyi/)
|
||||
* [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/)
|
||||
* [flake8-quotes](https://pypi.org/project/flake8-quotes/)
|
||||
* [flake8-raise](https://pypi.org/project/flake8-raise/)
|
||||
@@ -1791,6 +1874,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
* [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
* [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
* [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
* [flake8-django](https://pypi.org/project/flake8-django/) ([#2817](https://github.com/charliermarsh/ruff/issues/2817))
|
||||
* [flake8-docstrings](https://pypi.org/project/flake8-docstrings/)
|
||||
* [flake8-eradicate](https://pypi.org/project/flake8-eradicate/)
|
||||
* [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
@@ -1823,6 +1907,22 @@ implemented in [pyupgrade](https://pypi.org/project/pyupgrade/) ([#827](https://
|
||||
|
||||
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, feel free to file an Issue.
|
||||
|
||||
### What versions of Python does Ruff support?
|
||||
|
||||
Ruff can lint code for any Python version from 3.7 onwards. However, Ruff lacks support for a few
|
||||
language features that were introduced in Python 3.10 and later. Specifically, Ruff does not
|
||||
support:
|
||||
|
||||
- "Structural Pattern Matching" ([PEP 622](https://peps.python.org/pep-0622/)), introduced in Python 3.10.
|
||||
- "Exception Groups and except* ([PEP 654](https://www.python.org/dev/peps/pep-0654/)), introduced in Python 3.11.
|
||||
|
||||
Support for these features is planned.
|
||||
|
||||
Ruff does not support Python 2. Ruff _may_ run on pre-Python 3.7 code, although such versions
|
||||
are not officially supported (e.g., Ruff does _not_ respect type comments).
|
||||
|
||||
Ruff is installable under any Python version from 3.7 onwards.
|
||||
|
||||
### Do I need to install Rust to use Ruff?
|
||||
|
||||
Nope! Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
@@ -1975,6 +2075,15 @@ unfixable = ["B", "SIM", "TRY", "RUF"]
|
||||
|
||||
If you find a case where Ruff's autofix breaks your code, please file an Issue!
|
||||
|
||||
### How can I disable Ruff's color output?
|
||||
|
||||
Ruff's color output is powered by the [`colored`](https://crates.io/crates/colored) crate, which
|
||||
attempts to automatically detect whether the output stream supports color. However, you can force
|
||||
colors off by setting the `NO_COLOR` environment variable to any value (e.g., `NO_COLOR=1`).
|
||||
|
||||
[`colored`](https://crates.io/crates/colored) also supports the the `CLICOLOR` and `CLICOLOR_FORCE`
|
||||
environment variables (see the [spec](https://bixense.com/clicolors/)).
|
||||
|
||||
<!-- End section: FAQ -->
|
||||
|
||||
## Contributing
|
||||
@@ -2502,6 +2611,25 @@ select = ["E", "F", "B", "Q"]
|
||||
|
||||
---
|
||||
|
||||
#### [`show-fixes`](#show-fixes)
|
||||
|
||||
Whether to show an enumeration of all autofixed lint violations
|
||||
(overridden by the `--show-fixes` command-line flag).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# By default, always enumerate fixed violations.
|
||||
show-fixes = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`show-source`](#show-source)
|
||||
|
||||
Whether to show source code snippets when reporting lint violations
|
||||
@@ -3396,7 +3524,7 @@ alias (e.g., `import A as B`) to wrap such that every line contains
|
||||
exactly one member. For example, this formatting would be retained,
|
||||
rather than condensing to a single line:
|
||||
|
||||
```py
|
||||
```python
|
||||
from .utils import (
|
||||
test_directory as test_directory,
|
||||
test_id as test_id
|
||||
@@ -3458,6 +3586,24 @@ known-first-party = ["src"]
|
||||
|
||||
---
|
||||
|
||||
#### [`known-local-folder`](#known-local-folder)
|
||||
|
||||
A list of modules to consider being a local folder.
|
||||
Generally, this is reserved for relative imports (`from . import module`).
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `list[str]`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
known-local-folder = ["src"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`known-third-party`](#known-third-party)
|
||||
|
||||
A list of modules to consider third-party, regardless of whether they
|
||||
@@ -3479,7 +3625,7 @@ known-third-party = ["src"]
|
||||
#### [`lines-after-imports`](#lines-after-imports)
|
||||
|
||||
The number of blank lines to place after imports.
|
||||
-1 for automatic determination.
|
||||
Use `-1` for automatic determination.
|
||||
|
||||
**Default value**: `-1`
|
||||
|
||||
@@ -3495,6 +3641,24 @@ lines-after-imports = 1
|
||||
|
||||
---
|
||||
|
||||
#### [`lines-between-types`](#lines-between-types)
|
||||
|
||||
The number of lines to place between "direct" and `import from` imports.
|
||||
|
||||
**Default value**: `0`
|
||||
|
||||
**Type**: `int`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
# Use a single line between direct and from import
|
||||
lines-between-types = 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`no-lines-before`](#no-lines-before)
|
||||
|
||||
A list of sections that should _not_ be delineated from the previous
|
||||
@@ -3889,6 +4053,32 @@ keep-runtime-typing = true
|
||||
|
||||
<!-- End section: Settings -->
|
||||
|
||||
<!-- Begin section: Acknowledgements -->
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Ruff's linter draws on both the APIs and implementation details of many other
|
||||
tools in the Python ecosystem, especially [Flake8](https://github.com/PyCQA/flake8), [Pyflakes](https://github.com/PyCQA/pyflakes),
|
||||
[pycodestyle](https://github.com/PyCQA/pycodestyle), [pydocstyle](https://github.com/PyCQA/pydocstyle),
|
||||
[pyupgrade](https://github.com/asottile/pyupgrade), and [isort](https://github.com/PyCQA/isort).
|
||||
|
||||
In some cases, Ruff includes a "direct" Rust port of the corresponding tool.
|
||||
We're grateful to the maintainers of these tools for their work, and for all
|
||||
the value they've provided to the Python community.
|
||||
|
||||
Ruff's autoformatter is built on a fork of Rome's [`rome_formatter`](https://github.com/rome/tools/tree/main/crates/rome_formatter),
|
||||
and again draws on both the APIs and implementation details of [Rome](https://github.com/rome/tools),
|
||||
[Prettier](https://github.com/prettier/prettier), and [Black](https://github.com/psf/black).
|
||||
|
||||
Ruff is also influenced by a number of tools outside the Python ecosystem, like
|
||||
[Clippy](https://github.com/rust-lang/rust-clippy) and [ESLint](https://github.com/eslint/eslint).
|
||||
|
||||
Ruff is the beneficiary of a large number of [contributors](https://github.com/charliermarsh/ruff/graphs/contributors).
|
||||
|
||||
Ruff is released under the MIT license.
|
||||
|
||||
<!-- End section: Acknowledgements -->
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
[files]
|
||||
extend-exclude = ["snapshots"]
|
||||
extend-exclude = ["snapshots", "black"]
|
||||
|
||||
[default.extend-words]
|
||||
trivias = "trivias"
|
||||
hel = "hel"
|
||||
|
||||
7
clippy.toml
Normal file
7
clippy.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
doc-valid-idents = [
|
||||
"StackOverflow",
|
||||
"CodeQL",
|
||||
"IPython",
|
||||
"NumPy",
|
||||
"..",
|
||||
]
|
||||
@@ -1,19 +1,19 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.244"
|
||||
version = "0.0.247"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
colored = { version = "2.0.0" }
|
||||
configparser = { version = "3.0.2" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
regex = { version = "1.6.0" }
|
||||
once_cell = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
ruff = { path = "../ruff", default-features = false }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
toml = { version = "0.6.0" }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.244"
|
||||
version = "0.0.247"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -16,12 +16,12 @@ crate-type = ["cdylib", "rlib"]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.66" }
|
||||
anyhow = { workspace = true }
|
||||
bisection = { version = "0.1.0" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
cfg-if = { version = "1.0.0" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.1", features = ["derive", "env"] }
|
||||
clap = { workspace = true, features = ["derive", "env", "string"] }
|
||||
colored = { version = "2.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
@@ -29,32 +29,32 @@ glob = { version = "0.3.0" }
|
||||
globset = { version = "0.4.9" }
|
||||
ignore = { version = "0.4.18" }
|
||||
imperative = { version = "1.0.3" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
log = { version = "0.4.17" }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
once_cell = { workspace = true }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff_macros = { version = "0.0.244", path = "../ruff_macros" }
|
||||
ruff_python = { version = "0.0.244", path = "../ruff_python" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" }
|
||||
schemars = { version = "0.8.11" }
|
||||
regex = { workspace = true }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python = { path = "../ruff_python" }
|
||||
rustc-hash = { workspace = true }
|
||||
rustpython-common = { workspace = true }
|
||||
rustpython-parser = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde = { workspace = true }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
textwrap = { version = "0.16.0" }
|
||||
thiserror = { version = "1.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml = { version = "0.6.0" }
|
||||
toml = { workspace = true }
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
# For (future) wasm-pack support
|
||||
|
||||
4
crates/ruff/resources/test/disallowed_rule_names.txt
Normal file
4
crates/ruff/resources/test/disallowed_rule_names.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
avoid-*
|
||||
do-not-*
|
||||
uses-*
|
||||
*-used
|
||||
@@ -103,3 +103,7 @@ class Foo:
|
||||
@classmethod
|
||||
def foo(cls, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN101
|
||||
def foo(self, /, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
29
crates/ruff/resources/test/fixtures/flake8_bandit/S112.py
vendored
Normal file
29
crates/ruff/resources/test/fixtures/flake8_bandit/S112.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
continue
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception,):
|
||||
continue
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, ValueError):
|
||||
continue
|
||||
|
||||
try:
|
||||
pass
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError,):
|
||||
continue
|
||||
95
crates/ruff/resources/test/fixtures/flake8_bandit/S608.py
vendored
Normal file
95
crates/ruff/resources/test/fixtures/flake8_bandit/S608.py
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
# single-line failures
|
||||
query1 = "SELECT %s FROM table" % (var,) # bad
|
||||
query2 = "SELECT var FROM " + table
|
||||
query3 = "SELECT " + val + " FROM " + table
|
||||
query4 = "SELECT {} FROM table;".format(var)
|
||||
query5 = f"SELECT * FROM table WHERE var = {var}"
|
||||
|
||||
query6 = "DELETE FROM table WHERE var = %s" % (var,)
|
||||
query7 = "DELETE FROM table WHERE VAR = " + var
|
||||
query8 = "DELETE FROM " + table + "WHERE var = " + var
|
||||
query9 = "DELETE FROM table WHERE var = {}".format(var)
|
||||
query10 = f"DELETE FROM table WHERE var = {var}"
|
||||
|
||||
query11 = "INSERT INTO table VALUES (%s)" % (var,)
|
||||
query12 = "INSERT INTO TABLE VALUES (" + var + ")"
|
||||
query13 = "INSERT INTO {} VALUES ({})".format(table, var)
|
||||
query14 = f"INSERT INTO {table} VALUES var = {var}"
|
||||
|
||||
query15 = "UPDATE %s SET var = %s" % (table, var)
|
||||
query16 = "UPDATE " + table + " SET var = " + var
|
||||
query17 = "UPDATE {} SET var = {}".format(table, var)
|
||||
query18 = f"UPDATE {table} SET var = {var}"
|
||||
|
||||
query19 = "select %s from table" % (var,)
|
||||
query20 = "select var from " + table
|
||||
query21 = "select " + val + " from " + table
|
||||
query22 = "select {} from table;".format(var)
|
||||
query23 = f"select * from table where var = {var}"
|
||||
|
||||
query24 = "delete from table where var = %s" % (var,)
|
||||
query25 = "delete from table where var = " + var
|
||||
query26 = "delete from " + table + "where var = " + var
|
||||
query27 = "delete from table where var = {}".format(var)
|
||||
query28 = f"delete from table where var = {var}"
|
||||
|
||||
query29 = "insert into table values (%s)" % (var,)
|
||||
query30 = "insert into table values (" + var + ")"
|
||||
query31 = "insert into {} values ({})".format(table, var)
|
||||
query32 = f"insert into {table} values var = {var}"
|
||||
|
||||
query33 = "update %s set var = %s" % (table, var)
|
||||
query34 = "update " + table + " set var = " + var
|
||||
query35 = "update {} set var = {}".format(table, var)
|
||||
query36 = f"update {table} set var = {var}"
|
||||
|
||||
# multi-line failures
|
||||
def query37():
|
||||
return """
|
||||
SELECT *
|
||||
FROM table
|
||||
WHERE var = %s
|
||||
""" % var
|
||||
|
||||
def query38():
|
||||
return """
|
||||
SELECT *
|
||||
FROM TABLE
|
||||
WHERE var =
|
||||
""" + var
|
||||
|
||||
def query39():
|
||||
return """
|
||||
SELECT *
|
||||
FROM table
|
||||
WHERE var = {}
|
||||
""".format(var)
|
||||
|
||||
def query40():
|
||||
return f"""
|
||||
SELECT *
|
||||
FROM table
|
||||
WHERE var = {var}
|
||||
"""
|
||||
|
||||
def query41():
|
||||
return (
|
||||
"SELECT *"
|
||||
"FROM table"
|
||||
f"WHERE var = {var}"
|
||||
)
|
||||
|
||||
# # cursor-wrapped failures
|
||||
query42 = cursor.execute("SELECT * FROM table WHERE var = %s" % var)
|
||||
query43 = cursor.execute(f"SELECT * FROM table WHERE var = {var}")
|
||||
query44 = cursor.execute("SELECT * FROM table WHERE var = {}".format(var))
|
||||
query45 = cursor.executemany("SELECT * FROM table WHERE var = %s" % var, [])
|
||||
|
||||
# # pass
|
||||
query = "SELECT * FROM table WHERE id = 1"
|
||||
query = "DELETE FROM table WHERE id = 1"
|
||||
query = "INSERT INTO table VALUES (1)"
|
||||
query = "UPDATE table SET id = 1"
|
||||
cursor.execute('SELECT * FROM table WHERE id = %s', var)
|
||||
cursor.execute('SELECT * FROM table WHERE id = 1')
|
||||
cursor.executemany('SELECT * FROM table WHERE id = %s', [var, var2])
|
||||
@@ -17,6 +17,9 @@ s.rstrip(".facebook.com") # warning
|
||||
s.rstrip("e") # no warning
|
||||
s.rstrip("\n\t ") # no warning
|
||||
s.rstrip(r"\n\t ") # warning
|
||||
s.strip("a") # no warning
|
||||
s.strip("あ") # no warning
|
||||
s.strip("ああ") # warning
|
||||
|
||||
from somewhere import other_type, strip
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
Should emit:
|
||||
B904 - on lines 10, 11 and 16
|
||||
B904 - on lines 10, 11, 16, 62, and 64
|
||||
"""
|
||||
|
||||
try:
|
||||
@@ -53,3 +53,12 @@ except ImportError:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
raise
|
||||
|
||||
|
||||
try:
|
||||
...
|
||||
except Exception as e:
|
||||
if ...:
|
||||
raise RuntimeError("boom!")
|
||||
else:
|
||||
raise RuntimeError("bang!")
|
||||
|
||||
@@ -2,7 +2,9 @@ x = set(x for x in range(3))
|
||||
x = set(
|
||||
x for x in range(3)
|
||||
)
|
||||
|
||||
y = f'{set(a if a < 6 else 0 for a in range(3))}'
|
||||
_ = '{}'.format(set(a if a < 6 else 0 for a in range(3)))
|
||||
print(f'Hello {set(a for a in range(3))} World')
|
||||
|
||||
def set(*args, **kwargs):
|
||||
return None
|
||||
|
||||
@@ -3,3 +3,5 @@ dict(
|
||||
(x, x) for x in range(3)
|
||||
)
|
||||
dict(((x, x) for x in range(3)), z=3)
|
||||
y = f'{dict((x, x) for x in range(3))}'
|
||||
print(f'Hello {dict((x, x) for x in range(3))} World')
|
||||
|
||||
@@ -4,7 +4,9 @@ list(sorted(x))
|
||||
reversed(sorted(x))
|
||||
reversed(sorted(x, key=lambda e: e))
|
||||
reversed(sorted(x, reverse=True))
|
||||
|
||||
reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||
reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
reversed(sorted(x, reverse=False))
|
||||
|
||||
def reversed(*args, **kwargs):
|
||||
return None
|
||||
|
||||
@@ -12,3 +12,9 @@ sorted(list(x))
|
||||
sorted(tuple(x))
|
||||
sorted(sorted(x))
|
||||
sorted(reversed(x))
|
||||
tuple(
|
||||
list(
|
||||
[x, 3, "hell"\
|
||||
"o"]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
# Errors.
|
||||
nums = [1, 2, 3]
|
||||
map(lambda x: x + 1, nums)
|
||||
map(lambda x: str(x), nums)
|
||||
list(map(lambda x: x * 2, nums))
|
||||
set(map(lambda x: x % 2 == 0, nums))
|
||||
dict(map(lambda v: (v, v**2), nums))
|
||||
map(lambda: "const", nums)
|
||||
map(lambda _: 3.0, nums)
|
||||
_ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
all(map(lambda v: isinstance(v, dict), nums))
|
||||
filter(func, map(lambda v: v, nums))
|
||||
|
||||
# When inside f-string, then the fix should be surrounded by whitespace
|
||||
_ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
_ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
|
||||
# Error, but unfixable.
|
||||
# For simple expressions, this could be: `(x if x else 1 for x in nums)`.
|
||||
# For more complex expressions, this would differ: `(x + 2 if x else 3 for x in nums)`.
|
||||
map(lambda x=1: x, nums)
|
||||
|
||||
# False negatives.
|
||||
map(lambda x=2, y=1: x + y, nums, nums)
|
||||
set(map(lambda x, y: x, nums, nums))
|
||||
|
||||
|
||||
def myfunc(arg1: int, arg2: int = 4):
|
||||
return 2 * arg1 + arg2
|
||||
|
||||
|
||||
list(map(myfunc, nums))
|
||||
|
||||
[x for x in nums]
|
||||
|
||||
48
crates/ruff/resources/test/fixtures/flake8_django/DJ001.py
vendored
Normal file
48
crates/ruff/resources/test/fixtures/flake8_django/DJ001.py
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
from django.db.models import Model as DjangoModel
|
||||
from django.db import models
|
||||
from django.db.models import CharField as SmthCharField
|
||||
|
||||
|
||||
class IncorrectModel(models.Model):
|
||||
charfield = models.CharField(max_length=255, null=True)
|
||||
textfield = models.TextField(max_length=255, null=True)
|
||||
slugfield = models.SlugField(max_length=255, null=True)
|
||||
emailfield = models.EmailField(max_length=255, null=True)
|
||||
filepathfield = models.FilePathField(max_length=255, null=True)
|
||||
urlfield = models.URLField(max_length=255, null=True)
|
||||
|
||||
|
||||
class IncorrectModelWithAlias(DjangoModel):
|
||||
charfield = DjangoModel.CharField(max_length=255, null=True)
|
||||
textfield = SmthCharField(max_length=255, null=True)
|
||||
slugfield = models.SlugField(max_length=255, null=True)
|
||||
emailfield = models.EmailField(max_length=255, null=True)
|
||||
filepathfield = models.FilePathField(max_length=255, null=True)
|
||||
urlfield = models.URLField(max_length=255, null=True)
|
||||
|
||||
|
||||
class IncorrectModelWithoutSuperclass:
|
||||
charfield = DjangoModel.CharField(max_length=255, null=True)
|
||||
textfield = SmthCharField(max_length=255, null=True)
|
||||
slugfield = models.SlugField(max_length=255, null=True)
|
||||
emailfield = models.EmailField(max_length=255, null=True)
|
||||
filepathfield = models.FilePathField(max_length=255, null=True)
|
||||
urlfield = models.URLField(max_length=255, null=True)
|
||||
|
||||
|
||||
class CorrectModel(models.Model):
|
||||
charfield = models.CharField(max_length=255, null=False, blank=True)
|
||||
textfield = models.TextField(max_length=255, null=False, blank=True)
|
||||
slugfield = models.SlugField(max_length=255, null=False, blank=True)
|
||||
emailfield = models.EmailField(max_length=255, null=False, blank=True)
|
||||
filepathfield = models.FilePathField(max_length=255, null=False, blank=True)
|
||||
urlfield = models.URLField(max_length=255, null=False, blank=True)
|
||||
|
||||
charfieldu = models.CharField(max_length=255, null=True, blank=True, unique=True)
|
||||
textfieldu = models.TextField(max_length=255, null=True, blank=True, unique=True)
|
||||
slugfieldu = models.SlugField(max_length=255, null=True, blank=True, unique=True)
|
||||
emailfieldu = models.EmailField(max_length=255, null=True, blank=True, unique=True)
|
||||
filepathfieldu = models.FilePathField(
|
||||
max_length=255, null=True, blank=True, unique=True
|
||||
)
|
||||
urlfieldu = models.URLField(max_length=255, null=True, blank=True, unique=True)
|
||||
167
crates/ruff/resources/test/fixtures/flake8_django/DJ008.py
vendored
Normal file
167
crates/ruff/resources/test/fixtures/flake8_django/DJ008.py
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
from django.db import models
|
||||
from django.db.models import Model
|
||||
|
||||
|
||||
# Models without __str__
|
||||
class TestModel1(models.Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test model"
|
||||
verbose_name_plural = "test models"
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
class TestModel2(Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test model"
|
||||
verbose_name_plural = "test models"
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
class TestModel3(Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
abstract = False
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
# Models with __str__
|
||||
class TestModel4(Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test model"
|
||||
verbose_name_plural = "test models"
|
||||
|
||||
def __str__(self):
|
||||
return self.new_field
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
class TestModel5(models.Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test model"
|
||||
verbose_name_plural = "test models"
|
||||
|
||||
def __str__(self):
|
||||
return self.new_field
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
# Abstract models without str
|
||||
class AbstractTestModel1(models.Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
class AbstractTestModel2(Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
# Abstract models with __str__
|
||||
|
||||
|
||||
class AbstractTestModel3(Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.new_field
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
class AbstractTestModel4(models.Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.new_field
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
|
||||
|
||||
class AbstractTestModel5(models.Model):
|
||||
new_field = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
abstract = False
|
||||
|
||||
def __str__(self):
|
||||
return self.new_field
|
||||
|
||||
@property
|
||||
def my_brand_new_property(self):
|
||||
return 1
|
||||
|
||||
def my_beautiful_method(self):
|
||||
return 2
|
||||
37
crates/ruff/resources/test/fixtures/flake8_django/DJ013.py
vendored
Normal file
37
crates/ruff/resources/test/fixtures/flake8_django/DJ013.py
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from myapp.models import MyModel
|
||||
|
||||
test_decorator = lambda func: lambda *args, **kwargs: func(*args, **kwargs)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@test_decorator
|
||||
def correct_pre_save_handler():
|
||||
pass
|
||||
|
||||
|
||||
@test_decorator
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
def incorrect_pre_save_handler():
|
||||
pass
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@test_decorator
|
||||
def correct_multiple():
|
||||
pass
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
def correct_multiple():
|
||||
pass
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@test_decorator
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
def incorrect_multiple():
|
||||
pass
|
||||
13
crates/ruff/resources/test/fixtures/flake8_pyi/PYI001.py
vendored
Normal file
13
crates/ruff/resources/test/fixtures/flake8_pyi/PYI001.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from typing import ParamSpec, TypeVar, TypeVarTuple
|
||||
|
||||
T = TypeVar("T") # OK
|
||||
|
||||
TTuple = TypeVarTuple("TTuple") # OK
|
||||
|
||||
P = ParamSpec("P") # OK
|
||||
|
||||
_T = TypeVar("_T") # OK
|
||||
|
||||
_TTuple = TypeVarTuple("_TTuple") # OK
|
||||
|
||||
_P = ParamSpec("_P") # OK
|
||||
13
crates/ruff/resources/test/fixtures/flake8_pyi/PYI001.pyi
vendored
Normal file
13
crates/ruff/resources/test/fixtures/flake8_pyi/PYI001.pyi
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from typing import ParamSpec, TypeVar, TypeVarTuple
|
||||
|
||||
T = TypeVar("T") # Error: TypeVars in stubs must start with _
|
||||
|
||||
TTuple = TypeVarTuple("TTuple") # Error: TypeVarTuples must also start with _
|
||||
|
||||
P = ParamSpec("P") # Error: ParamSpecs must start with _
|
||||
|
||||
_T = TypeVar("_T") # OK
|
||||
|
||||
_TTuple = TypeVarTuple("_TTuple") # OK
|
||||
|
||||
_P = ParamSpec("_P") # OK
|
||||
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI007.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI007.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import sys
|
||||
|
||||
if sys.platform == "platform_name_1": ... # OK
|
||||
|
||||
if sys.platform != "platform_name_2": ... # OK
|
||||
|
||||
if sys.platform in ["linux"]: ... # OK
|
||||
|
||||
if sys.platform > 3: ... # OK
|
||||
|
||||
if sys.platform == 10.12: ... # OK
|
||||
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI007.pyi
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI007.pyi
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import sys
|
||||
|
||||
if sys.platform == "platform_name_1": ... # OK
|
||||
|
||||
if sys.platform != "platform_name_2": ... # OK
|
||||
|
||||
if sys.platform in ["linux"]: ... # Error: PYI007 Unrecognized sys.platform check
|
||||
|
||||
if sys.platform > 3: ... # Error: PYI007 Unrecognized sys.platform check
|
||||
|
||||
if sys.platform == 10.12: ... # Error: PYI007 Unrecognized sys.platform check
|
||||
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI008.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI008.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import sys
|
||||
|
||||
if sys.platform == "linus": ... # OK
|
||||
|
||||
if sys.platform != "linux": ... # OK
|
||||
|
||||
if sys.platform == "win32": ... # OK
|
||||
|
||||
if sys.platform != "darwin": ... # OK
|
||||
|
||||
if sys.platform == "cygwin": ... # OK
|
||||
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI008.pyi
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_pyi/PYI008.pyi
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import sys
|
||||
|
||||
if sys.platform == "linus": ... # Error: PYI008 Unrecognized platform `linus`
|
||||
|
||||
if sys.platform != "linux": ... # OK
|
||||
|
||||
if sys.platform == "win32": ... # OK
|
||||
|
||||
if sys.platform != "darwin": ... # OK
|
||||
|
||||
if sys.platform == "cygwin": ... # OK
|
||||
@@ -1,3 +1,14 @@
|
||||
import builtins
|
||||
import os
|
||||
import posix
|
||||
from posix import abort
|
||||
import sys as std_sys
|
||||
import _thread
|
||||
import _winapi
|
||||
|
||||
import pytest
|
||||
from pytest import xfail as py_xfail
|
||||
|
||||
###
|
||||
# Errors
|
||||
###
|
||||
@@ -39,6 +50,20 @@ def x(y):
|
||||
print() # error
|
||||
|
||||
|
||||
# A nonexistent function
|
||||
def func_unknown(x):
|
||||
if x > 0:
|
||||
return False
|
||||
no_such_function() # error
|
||||
|
||||
|
||||
# A function that does return the control
|
||||
def func_no_noreturn(x):
|
||||
if x > 0:
|
||||
return False
|
||||
print("", end="") # error
|
||||
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
@@ -123,3 +148,106 @@ def prompts(self, foo):
|
||||
for x in foo:
|
||||
yield x
|
||||
yield x + 1
|
||||
|
||||
|
||||
# Functions that never return
|
||||
def noreturn_exit(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
exit()
|
||||
|
||||
|
||||
def noreturn_quit(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
quit()
|
||||
|
||||
|
||||
def noreturn_builtins_exit(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
builtins.exit()
|
||||
|
||||
|
||||
def noreturn_builtins_quit(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
builtins.quit()
|
||||
|
||||
|
||||
def noreturn_os__exit(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
os._exit(0)
|
||||
|
||||
|
||||
def noreturn_os_abort(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
os.abort()
|
||||
|
||||
|
||||
def noreturn_posix__exit():
|
||||
if x > 0:
|
||||
return 1
|
||||
posix._exit()
|
||||
|
||||
|
||||
def noreturn_posix_abort():
|
||||
if x > 0:
|
||||
return 1
|
||||
posix.abort()
|
||||
|
||||
|
||||
def noreturn_posix_abort_2():
|
||||
if x > 0:
|
||||
return 1
|
||||
abort()
|
||||
|
||||
|
||||
def noreturn_sys_exit():
|
||||
if x > 0:
|
||||
return 1
|
||||
std_sys.exit(0)
|
||||
|
||||
|
||||
def noreturn__thread_exit():
|
||||
if x > 0:
|
||||
return 1
|
||||
_thread.exit(0)
|
||||
|
||||
|
||||
def noreturn__winapi_exitprocess():
|
||||
if x > 0:
|
||||
return 1
|
||||
_winapi.ExitProcess(0)
|
||||
|
||||
|
||||
def noreturn_pytest_exit():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.exit("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_fail():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.fail("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_skip():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.skip("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_xfail():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.xfail("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_xfail_2():
|
||||
if x > 0:
|
||||
return 1
|
||||
py_xfail("oof")
|
||||
|
||||
@@ -136,3 +136,24 @@ def nested2(x, y, z):
|
||||
break
|
||||
else:
|
||||
a = z
|
||||
|
||||
|
||||
def elif1(x, y, w, z):
|
||||
for i in x:
|
||||
if i > y:
|
||||
a = z
|
||||
elif i > w:
|
||||
break
|
||||
else:
|
||||
a = z
|
||||
|
||||
|
||||
def elif2(x, y, w, z):
|
||||
for i in x:
|
||||
if i > y:
|
||||
a = z
|
||||
else:
|
||||
if i > w:
|
||||
break
|
||||
else:
|
||||
a = z
|
||||
|
||||
@@ -33,16 +33,25 @@ class Foo(metaclass=BazMeta):
|
||||
def get_bar():
|
||||
if self.bar._private: # SLF001
|
||||
return None
|
||||
if self.bar()._private: # SLF001
|
||||
return None
|
||||
if Bar._private_thing: # SLF001
|
||||
return None
|
||||
if Foo._private_thing:
|
||||
return None
|
||||
Foo = Bar()
|
||||
if Foo._private_thing: # SLF001
|
||||
return None
|
||||
return self.bar
|
||||
|
||||
def public_func(self):
|
||||
pass
|
||||
super().public_func()
|
||||
|
||||
def _private_func(self):
|
||||
pass
|
||||
super()._private_func()
|
||||
|
||||
def __really_private_func(self, arg):
|
||||
pass
|
||||
super().__really_private_func(arg)
|
||||
|
||||
|
||||
foo = Foo()
|
||||
@@ -51,9 +60,12 @@ print(foo.public_thing)
|
||||
print(foo.public_func())
|
||||
print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
print(foo().__class__)
|
||||
|
||||
print(foo._private_thing) # SLF001
|
||||
print(foo.__really_private_thing) # SLF001
|
||||
print(foo._private_func()) # SLF001
|
||||
print(foo.__really_private_func(1)) # SLF001
|
||||
print(foo.bar._private) # SLF001
|
||||
print(foo()._private_thing) # SLF001
|
||||
print(foo()._private_thing__) # SLF001
|
||||
|
||||
@@ -155,3 +155,19 @@ def f():
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if x not in y:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if x > y:
|
||||
return False
|
||||
return True
|
||||
|
||||
96
crates/ruff/resources/test/fixtures/flake8_simplify/SIM114.py
vendored
Normal file
96
crates/ruff/resources/test/fixtures/flake8_simplify/SIM114.py
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
# Errors
|
||||
if a:
|
||||
b
|
||||
elif c:
|
||||
b
|
||||
|
||||
if x == 1:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
elif x == 2:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
|
||||
if x == 1:
|
||||
if True:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
elif x == 2:
|
||||
if True:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
|
||||
if x == 1:
|
||||
if True:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
elif False:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
elif x == 2:
|
||||
if True:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
elif False:
|
||||
for _ in range(20):
|
||||
print("hello")
|
||||
|
||||
if (
|
||||
x == 1
|
||||
and y == 2
|
||||
and z == 3
|
||||
and a == 4
|
||||
and b == 5
|
||||
and c == 6
|
||||
and d == 7
|
||||
and e == 8
|
||||
and f == 9
|
||||
and g == 10
|
||||
and h == 11
|
||||
and i == 12
|
||||
and j == 13
|
||||
and k == 14
|
||||
):
|
||||
pass
|
||||
elif 1 == 2:
|
||||
pass
|
||||
|
||||
if result.eofs == "O":
|
||||
pass
|
||||
elif result.eofs == "S":
|
||||
skipped = 1
|
||||
elif result.eofs == "F":
|
||||
errors = 1
|
||||
elif result.eofs == "E":
|
||||
errors = 1
|
||||
|
||||
|
||||
# OK
|
||||
def complicated_calc(*arg, **kwargs):
|
||||
return 42
|
||||
|
||||
|
||||
def foo(p):
|
||||
if p == 2:
|
||||
return complicated_calc(microsecond=0)
|
||||
elif p == 3:
|
||||
return complicated_calc(microsecond=0, second=0)
|
||||
return None
|
||||
|
||||
|
||||
a = False
|
||||
b = True
|
||||
c = True
|
||||
|
||||
if a:
|
||||
z = 1
|
||||
elif b:
|
||||
z = 2
|
||||
elif c:
|
||||
z = 1
|
||||
|
||||
# False negative (or arguably a different rule)
|
||||
if result.eofs == "F":
|
||||
errors = 1
|
||||
else:
|
||||
errors = 1
|
||||
@@ -1,12 +1,28 @@
|
||||
from . import sibling
|
||||
from .sibling import example
|
||||
|
||||
from .. import parent
|
||||
from ..parent import example
|
||||
|
||||
from ... import grandparent
|
||||
from ...grandparent import example
|
||||
|
||||
# OK
|
||||
import other
|
||||
import other.example
|
||||
from other import example
|
||||
|
||||
# TID252
|
||||
from . import sibling
|
||||
from .sibling import example
|
||||
from .. import parent
|
||||
from ..parent import example
|
||||
from ... import grandparent
|
||||
from ...grandparent import example
|
||||
from .parent import hello
|
||||
from .\
|
||||
parent import \
|
||||
hello_world
|
||||
from \
|
||||
..parent\
|
||||
import \
|
||||
world_hello
|
||||
from ..... import ultragrantparent
|
||||
from ...... import ultragrantparent
|
||||
from ....... import ultragrantparent
|
||||
from ......... import ultragrantparent
|
||||
from ........................... import ultragrantparent
|
||||
from .....parent import ultragrantparent
|
||||
from .........parent import ultragrantparent
|
||||
from ...........................parent import ultragrantparent
|
||||
|
||||
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/sublib/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/sublib/__init__.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from typing import TYPE_CHECKING, Any, ClassVar
|
||||
|
||||
import attrs
|
||||
|
||||
from ..protocol import commands, definitions, responses
|
||||
from ..server import example
|
||||
from . import logger, models
|
||||
@@ -44,9 +44,9 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd
|
||||
import pandas as pd # TCH002
|
||||
|
||||
x = dict["pd.DataFrame", "pd.DataFrame"] # TCH002
|
||||
x = dict["pd.DataFrame", "pd.DataFrame"]
|
||||
|
||||
|
||||
def f():
|
||||
|
||||
6
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_11.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_11.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import List
|
||||
|
||||
__all__ = ("List",)
|
||||
8
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_12.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_12.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, TYPE_CHECKING, TypeAlias
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
AnyCallable: TypeAlias = Callable[..., Any]
|
||||
10
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_13.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_13.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Final, Literal, TypeAlias
|
||||
|
||||
RatingKey: TypeAlias = Literal["good", "fair", "poor"]
|
||||
|
||||
RATING_KEYS: Final[tuple[RatingKey, ...]] = ("good", "fair", "poor")
|
||||
16
crates/ruff/resources/test/fixtures/isort/lines_between_types.py
vendored
Normal file
16
crates/ruff/resources/test/fixtures/isort/lines_between_types.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
|
||||
from binascii import hexlify
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
from sanic import Sanic
|
||||
from loguru import Logger
|
||||
|
||||
from . import config
|
||||
from .data import Data
|
||||
@@ -3,3 +3,5 @@ line-length = 88
|
||||
|
||||
[tool.ruff.isort]
|
||||
lines-after-imports = 3
|
||||
lines-between-types = 2
|
||||
known-local-folder = ["ruff"]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
import ruff
|
||||
import leading_prefix
|
||||
import os
|
||||
from . import leading_prefix
|
||||
|
||||
20
crates/ruff/resources/test/fixtures/numpy/NPY001.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/numpy/NPY001.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import numpy as npy
|
||||
import numpy as np
|
||||
import numpy
|
||||
|
||||
# Error
|
||||
npy.bool
|
||||
npy.int
|
||||
|
||||
if dtype == np.object:
|
||||
...
|
||||
|
||||
result = result.select_dtypes([np.byte, np.ubyte, np.short, np.ushort, np.int, np.long])
|
||||
|
||||
pdf = pd.DataFrame(
|
||||
data=[[1, 2, 3]],
|
||||
columns=["a", "b", "c"],
|
||||
dtype=numpy.object,
|
||||
)
|
||||
|
||||
_ = arr.astype(np.int)
|
||||
56
crates/ruff/resources/test/fixtures/pycodestyle/E70.py
vendored
Normal file
56
crates/ruff/resources/test/fixtures/pycodestyle/E70.py
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
#: E701:1:5
|
||||
if a: a = False
|
||||
#: E701:1:40
|
||||
if not header or header[:6] != 'bytes=': return
|
||||
#: E702:1:10
|
||||
a = False; b = True
|
||||
#: E702:1:17
|
||||
import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe)
|
||||
#: E703:1:13
|
||||
import shlex;
|
||||
#: E702:1:9 E703:1:23
|
||||
del a[:]; a.append(42);
|
||||
#: E704:1:1
|
||||
def f(x): return 2
|
||||
#: E704:1:1
|
||||
async def f(x): return 2
|
||||
#: E704:1:1 E271:1:6
|
||||
async def f(x): return 2
|
||||
#: E704:1:1 E226:1:19
|
||||
def f(x): return 2*x
|
||||
#: E704:2:5 E226:2:23
|
||||
while all is round:
|
||||
def f(x): return 2*x
|
||||
#: E704:1:8 E702:1:11 E703:1:14
|
||||
if True: x; y;
|
||||
#: E701:1:8
|
||||
if True: lambda a: b
|
||||
#: E701:1:10
|
||||
if a := 1: pass
|
||||
# E701:1:4 E701:2:18 E701:3:8
|
||||
try: lambda foo: bar
|
||||
except ValueError: pass
|
||||
finally: pass
|
||||
# E701:1:7
|
||||
class C: pass
|
||||
# E701:1:7
|
||||
with C(): pass
|
||||
# E701:1:14
|
||||
async with C(): pass
|
||||
#:
|
||||
lambda a: b
|
||||
#:
|
||||
a: List[str] = []
|
||||
#:
|
||||
if a := 1:
|
||||
pass
|
||||
#:
|
||||
func = lambda x: x** 2 if cond else lambda x:x
|
||||
#:
|
||||
class C: ...
|
||||
#:
|
||||
def f(): ...
|
||||
#: E701:1:8 E702:1:13
|
||||
class C: ...; x = 1
|
||||
#: E701:1:8 E702:1:13
|
||||
class C: ...; ...
|
||||
@@ -5,17 +5,16 @@ f = lambda x: 2 * x
|
||||
#: E731
|
||||
while False:
|
||||
this = lambda y, z: 2 * x
|
||||
|
||||
#: E731
|
||||
f = lambda: (yield 1)
|
||||
#: E731
|
||||
f = lambda: (yield from g())
|
||||
|
||||
f = object()
|
||||
f.method = lambda: "Method"
|
||||
|
||||
f = {}
|
||||
f["a"] = lambda x: x ** 2
|
||||
|
||||
f["a"] = lambda x: x**2
|
||||
f = []
|
||||
f.append(lambda x: x ** 2)
|
||||
|
||||
f = g = lambda x: x ** 2
|
||||
|
||||
f.append(lambda x: x**2)
|
||||
f = g = lambda x: x**2
|
||||
lambda: "no-op"
|
||||
|
||||
4
crates/ruff/resources/test/fixtures/pyflakes/F401_9.py
vendored
Normal file
4
crates/ruff/resources/test/fixtures/pyflakes/F401_9.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
"""Test: late-binding of `__all__`."""
|
||||
|
||||
__all__ = ("bar",)
|
||||
from foo import bar, baz
|
||||
7
crates/ruff/resources/test/fixtures/pyflakes/F822_2.py
vendored
Normal file
7
crates/ruff/resources/test/fixtures/pyflakes/F822_2.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
__all__ = ["foo"]
|
||||
|
||||
foo = 1
|
||||
|
||||
|
||||
def bar():
|
||||
pass
|
||||
@@ -66,3 +66,40 @@ def f(a, b):
|
||||
|
||||
y = \
|
||||
a if a is not None else b
|
||||
|
||||
|
||||
def f():
|
||||
with Nested(m) as (cm):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
with (Nested(m) as (cm),):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
with Nested(m) as (x, y):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
toplevel = tt = lexer.get_token()
|
||||
if not tt:
|
||||
break
|
||||
|
||||
|
||||
def f():
|
||||
toplevel = tt = lexer.get_token()
|
||||
|
||||
|
||||
def f():
|
||||
toplevel = (a, b) = lexer.get_token()
|
||||
|
||||
|
||||
def f():
|
||||
(a, b) = toplevel = lexer.get_token()
|
||||
|
||||
|
||||
def f():
|
||||
toplevel = tt = 1
|
||||
|
||||
57
crates/ruff/resources/test/fixtures/pylint/bad_string_format_type.py
vendored
Normal file
57
crates/ruff/resources/test/fixtures/pylint/bad_string_format_type.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
# Errors
|
||||
print("foo %(foo)d bar %(bar)d" % {"foo": "1", "bar": "2"})
|
||||
|
||||
"foo %e bar %s" % ("1", 2)
|
||||
|
||||
"%d" % "1"
|
||||
"%o" % "1"
|
||||
"%(key)d" % {"key": "1"}
|
||||
"%x" % 1.1
|
||||
"%(key)x" % {"key": 1.1}
|
||||
"%d" % []
|
||||
"%d" % ([],)
|
||||
"%(key)d" % {"key": []}
|
||||
print("%d" % ("%s" % ("nested",),))
|
||||
"%d" % ((1, 2, 3),)
|
||||
|
||||
# False negatives
|
||||
WORD = "abc"
|
||||
"%d" % WORD
|
||||
"%d %s" % (WORD, WORD)
|
||||
VALUES_TO_FORMAT = (1, "2", 3.0)
|
||||
"%d %d %f" % VALUES_TO_FORMAT
|
||||
|
||||
# OK
|
||||
"%d %s %f" % VALUES_TO_FORMAT
|
||||
"%s" % "1"
|
||||
"%s %s %s" % ("1", 2, 3.5)
|
||||
print("%d %d"
|
||||
%
|
||||
(1, 1.1))
|
||||
"%s" % 1
|
||||
"%d" % 1
|
||||
"%f" % 1
|
||||
"%s" % 1
|
||||
"%(key)s" % {"key": 1}
|
||||
"%d" % 1
|
||||
"%(key)d" % {"key": 1}
|
||||
"%f" % 1
|
||||
"%(key)f" % {"key": 1}
|
||||
"%d" % 1.1
|
||||
"%(key)d" % {"key": 1.1}
|
||||
"%s" % []
|
||||
"%(key)s" % {"key": []}
|
||||
"%s" % None
|
||||
"%(key)s" % {"key": None}
|
||||
print("%s" % ("%s" % ("nested",),))
|
||||
print("%s" % ("%d" % (5,),))
|
||||
"%d %d" % "1"
|
||||
"%d" "%d" % "1"
|
||||
"-%f" % time.time()
|
||||
"%r" % (object['dn'],)
|
||||
r'\%03o' % (ord(c),)
|
||||
('%02X' % int(_) for _ in o)
|
||||
"%s;range=%d-*" % (attr, upper + 1)
|
||||
"%d" % (len(foo),)
|
||||
'(%r, %r, %r, %r)' % (hostname, address, username, '$PASSWORD')
|
||||
'%r' % ({'server_school_roles': server_school_roles, 'is_school_multiserver_domain': is_school_multiserver_domain}, )
|
||||
@@ -1,6 +1,3 @@
|
||||
# E2502
|
||||
print("\u202B\u202E\u05e9\u05DC\u05D5\u05DD\u202C")
|
||||
|
||||
# E2502
|
||||
print("שלום")
|
||||
|
||||
@@ -20,5 +17,12 @@ def subtract_funds(account: str, amount: int):
|
||||
return
|
||||
|
||||
|
||||
# OK
|
||||
print("\u202B\u202E\u05e9\u05DC\u05D5\u05DD\u202C")
|
||||
|
||||
|
||||
# OK
|
||||
print("\N{RIGHT-TO-LEFT MARK}")
|
||||
|
||||
# OK
|
||||
print("Hello World")
|
||||
|
||||
17
crates/ruff/resources/test/fixtures/pylint/yield_in_init.py
vendored
Normal file
17
crates/ruff/resources/test/fixtures/pylint/yield_in_init.py
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
def a():
|
||||
yield
|
||||
|
||||
def __init__():
|
||||
yield
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
yield
|
||||
|
||||
|
||||
class B:
|
||||
def __init__(self):
|
||||
yield from self.gen()
|
||||
|
||||
def gen(self):
|
||||
yield 5
|
||||
52
crates/ruff/resources/test/fixtures/ruff/RUF006.py
vendored
Normal file
52
crates/ruff/resources/test/fixtures/ruff/RUF006.py
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import asyncio
|
||||
|
||||
|
||||
# Error
|
||||
def f():
|
||||
asyncio.create_task(coordinator.ws_connect()) # Error
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
background_tasks = set()
|
||||
|
||||
for i in range(10):
|
||||
task = asyncio.create_task(some_coro(param=i))
|
||||
|
||||
# Add task to the set. This creates a strong reference.
|
||||
background_tasks.add(task)
|
||||
|
||||
# To prevent keeping references to finished tasks forever,
|
||||
# make each task remove its own reference from the set after
|
||||
# completion:
|
||||
task.add_done_callback(background_tasks.discard)
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
ctx.task = asyncio.create_task(make_request())
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
tasks.append(asyncio.create_task(self._populate_collection(coll, coll_info)))
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
asyncio.wait([asyncio.create_task(client.close()) for client in clients.values()])
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
tasks = [asyncio.create_task(task) for task in tasks]
|
||||
|
||||
|
||||
# Ok (false negative)
|
||||
def f():
|
||||
task = asyncio.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# Ok (potential false negative)
|
||||
def f():
|
||||
do_nothing_with_the_task(asyncio.create_task(coordinator.ws_connect()))
|
||||
@@ -27,6 +27,7 @@ def good():
|
||||
logger.exception("process failed")
|
||||
raise
|
||||
|
||||
|
||||
def still_good():
|
||||
try:
|
||||
process()
|
||||
@@ -35,6 +36,14 @@ def still_good():
|
||||
raise
|
||||
|
||||
|
||||
def still_good_too():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
print(e)
|
||||
raise e from None
|
||||
|
||||
|
||||
def still_actually_good():
|
||||
try:
|
||||
process()
|
||||
@@ -60,5 +69,6 @@ def bad_that_needs_recursion_2():
|
||||
except MyException as e:
|
||||
logger.exception("process failed")
|
||||
if True:
|
||||
|
||||
def foo():
|
||||
raise e
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Expr, ExprContext, ExprKind, Keyword,
|
||||
Operator, Unaryop,
|
||||
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
|
||||
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, Operator, Stmt, StmtKind, Unaryop,
|
||||
Withitem,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
@@ -126,6 +127,36 @@ impl From<&Cmpop> for ComparableCmpop {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ComparableAlias<'a> {
|
||||
pub name: &'a str,
|
||||
pub asname: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Alias> for ComparableAlias<'a> {
|
||||
fn from(alias: &'a Alias) -> Self {
|
||||
Self {
|
||||
name: &alias.node.name,
|
||||
asname: alias.node.asname.as_deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ComparableWithitem<'a> {
|
||||
pub context_expr: ComparableExpr<'a>,
|
||||
pub optional_vars: Option<ComparableExpr<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Withitem> for ComparableWithitem<'a> {
|
||||
fn from(withitem: &'a Withitem) -> Self {
|
||||
Self {
|
||||
context_expr: (&withitem.context_expr).into(),
|
||||
optional_vars: withitem.optional_vars.as_ref().map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ComparableConstant<'a> {
|
||||
None,
|
||||
@@ -147,9 +178,7 @@ impl<'a> From<&'a Constant> for ComparableConstant<'a> {
|
||||
Constant::Str(value) => Self::Str(value),
|
||||
Constant::Bytes(value) => Self::Bytes(value),
|
||||
Constant::Int(value) => Self::Int(value),
|
||||
Constant::Tuple(value) => {
|
||||
Self::Tuple(value.iter().map(std::convert::Into::into).collect())
|
||||
}
|
||||
Constant::Tuple(value) => Self::Tuple(value.iter().map(Into::into).collect()),
|
||||
Constant::Float(value) => Self::Float(value.to_bits()),
|
||||
Constant::Complex { real, imag } => Self::Complex {
|
||||
real: real.to_bits(),
|
||||
@@ -174,37 +203,23 @@ pub struct ComparableArguments<'a> {
|
||||
impl<'a> From<&'a Arguments> for ComparableArguments<'a> {
|
||||
fn from(arguments: &'a Arguments) -> Self {
|
||||
Self {
|
||||
posonlyargs: arguments
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect(),
|
||||
args: arguments
|
||||
.args
|
||||
.iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect(),
|
||||
vararg: arguments.vararg.as_ref().map(std::convert::Into::into),
|
||||
kwonlyargs: arguments
|
||||
.kwonlyargs
|
||||
.iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect(),
|
||||
kw_defaults: arguments
|
||||
.kw_defaults
|
||||
.iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect(),
|
||||
kwarg: arguments.vararg.as_ref().map(std::convert::Into::into),
|
||||
defaults: arguments
|
||||
.defaults
|
||||
.iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect(),
|
||||
posonlyargs: arguments.posonlyargs.iter().map(Into::into).collect(),
|
||||
args: arguments.args.iter().map(Into::into).collect(),
|
||||
vararg: arguments.vararg.as_ref().map(Into::into),
|
||||
kwonlyargs: arguments.kwonlyargs.iter().map(Into::into).collect(),
|
||||
kw_defaults: arguments.kw_defaults.iter().map(Into::into).collect(),
|
||||
kwarg: arguments.vararg.as_ref().map(Into::into),
|
||||
defaults: arguments.defaults.iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Box<Arguments>> for ComparableArguments<'a> {
|
||||
fn from(arguments: &'a Box<Arguments>) -> Self {
|
||||
(&**arguments).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Box<Arg>> for ComparableArg<'a> {
|
||||
fn from(arg: &'a Box<Arg>) -> Self {
|
||||
(&**arg).into()
|
||||
@@ -222,7 +237,7 @@ impl<'a> From<&'a Arg> for ComparableArg<'a> {
|
||||
fn from(arg: &'a Arg) -> Self {
|
||||
Self {
|
||||
arg: &arg.node.arg,
|
||||
annotation: arg.node.annotation.as_ref().map(std::convert::Into::into),
|
||||
annotation: arg.node.annotation.as_ref().map(Into::into),
|
||||
type_comment: arg.node.type_comment.as_deref(),
|
||||
}
|
||||
}
|
||||
@@ -256,16 +271,32 @@ impl<'a> From<&'a Comprehension> for ComparableComprehension<'a> {
|
||||
Self {
|
||||
target: (&comprehension.target).into(),
|
||||
iter: (&comprehension.iter).into(),
|
||||
ifs: comprehension
|
||||
.ifs
|
||||
.iter()
|
||||
.map(std::convert::Into::into)
|
||||
.collect(),
|
||||
ifs: comprehension.ifs.iter().map(Into::into).collect(),
|
||||
is_async: &comprehension.is_async,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ComparableExcepthandler<'a> {
|
||||
ExceptHandler {
|
||||
type_: Option<ComparableExpr<'a>>,
|
||||
name: Option<&'a str>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Excepthandler> for ComparableExcepthandler<'a> {
|
||||
fn from(excepthandler: &'a Excepthandler) -> Self {
|
||||
let ExcepthandlerKind::ExceptHandler { type_, name, body } = &excepthandler.node;
|
||||
Self::ExceptHandler {
|
||||
type_: type_.as_ref().map(Into::into),
|
||||
name: name.as_deref(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ComparableExpr<'a> {
|
||||
BoolOp {
|
||||
@@ -399,7 +430,7 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
match &expr.node {
|
||||
ExprKind::BoolOp { op, values } => Self::BoolOp {
|
||||
op: op.into(),
|
||||
values: values.iter().map(std::convert::Into::into).collect(),
|
||||
values: values.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::NamedExpr { target, value } => Self::NamedExpr {
|
||||
target: target.into(),
|
||||
@@ -426,20 +457,20 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
ExprKind::Dict { keys, values } => Self::Dict {
|
||||
keys: keys
|
||||
.iter()
|
||||
.map(|expr| expr.as_ref().map(std::convert::Into::into))
|
||||
.map(|expr| expr.as_ref().map(Into::into))
|
||||
.collect(),
|
||||
values: values.iter().map(std::convert::Into::into).collect(),
|
||||
values: values.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::Set { elts } => Self::Set {
|
||||
elts: elts.iter().map(std::convert::Into::into).collect(),
|
||||
elts: elts.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::ListComp { elt, generators } => Self::ListComp {
|
||||
elt: elt.into(),
|
||||
generators: generators.iter().map(std::convert::Into::into).collect(),
|
||||
generators: generators.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::SetComp { elt, generators } => Self::SetComp {
|
||||
elt: elt.into(),
|
||||
generators: generators.iter().map(std::convert::Into::into).collect(),
|
||||
generators: generators.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::DictComp {
|
||||
key,
|
||||
@@ -448,17 +479,17 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
} => Self::DictComp {
|
||||
key: key.into(),
|
||||
value: value.into(),
|
||||
generators: generators.iter().map(std::convert::Into::into).collect(),
|
||||
generators: generators.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::GeneratorExp { elt, generators } => Self::GeneratorExp {
|
||||
elt: elt.into(),
|
||||
generators: generators.iter().map(std::convert::Into::into).collect(),
|
||||
generators: generators.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::Await { value } => Self::Await {
|
||||
value: value.into(),
|
||||
},
|
||||
ExprKind::Yield { value } => Self::Yield {
|
||||
value: value.as_ref().map(std::convert::Into::into),
|
||||
value: value.as_ref().map(Into::into),
|
||||
},
|
||||
ExprKind::YieldFrom { value } => Self::YieldFrom {
|
||||
value: value.into(),
|
||||
@@ -469,8 +500,8 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
comparators,
|
||||
} => Self::Compare {
|
||||
left: left.into(),
|
||||
ops: ops.iter().map(std::convert::Into::into).collect(),
|
||||
comparators: comparators.iter().map(std::convert::Into::into).collect(),
|
||||
ops: ops.iter().map(Into::into).collect(),
|
||||
comparators: comparators.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::Call {
|
||||
func,
|
||||
@@ -478,8 +509,8 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
keywords,
|
||||
} => Self::Call {
|
||||
func: func.into(),
|
||||
args: args.iter().map(std::convert::Into::into).collect(),
|
||||
keywords: keywords.iter().map(std::convert::Into::into).collect(),
|
||||
args: args.iter().map(Into::into).collect(),
|
||||
keywords: keywords.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::FormattedValue {
|
||||
value,
|
||||
@@ -488,10 +519,10 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
} => Self::FormattedValue {
|
||||
value: value.into(),
|
||||
conversion,
|
||||
format_spec: format_spec.as_ref().map(std::convert::Into::into),
|
||||
format_spec: format_spec.as_ref().map(Into::into),
|
||||
},
|
||||
ExprKind::JoinedStr { values } => Self::JoinedStr {
|
||||
values: values.iter().map(std::convert::Into::into).collect(),
|
||||
values: values.iter().map(Into::into).collect(),
|
||||
},
|
||||
ExprKind::Constant { value, kind } => Self::Constant {
|
||||
value: value.into(),
|
||||
@@ -516,18 +547,314 @@ impl<'a> From<&'a Expr> for ComparableExpr<'a> {
|
||||
ctx: ctx.into(),
|
||||
},
|
||||
ExprKind::List { elts, ctx } => Self::List {
|
||||
elts: elts.iter().map(std::convert::Into::into).collect(),
|
||||
elts: elts.iter().map(Into::into).collect(),
|
||||
ctx: ctx.into(),
|
||||
},
|
||||
ExprKind::Tuple { elts, ctx } => Self::Tuple {
|
||||
elts: elts.iter().map(std::convert::Into::into).collect(),
|
||||
elts: elts.iter().map(Into::into).collect(),
|
||||
ctx: ctx.into(),
|
||||
},
|
||||
ExprKind::Slice { lower, upper, step } => Self::Slice {
|
||||
lower: lower.as_ref().map(std::convert::Into::into),
|
||||
upper: upper.as_ref().map(std::convert::Into::into),
|
||||
step: step.as_ref().map(std::convert::Into::into),
|
||||
lower: lower.as_ref().map(Into::into),
|
||||
upper: upper.as_ref().map(Into::into),
|
||||
step: step.as_ref().map(Into::into),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ComparableStmt<'a> {
|
||||
FunctionDef {
|
||||
name: &'a str,
|
||||
args: ComparableArguments<'a>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
decorator_list: Vec<ComparableExpr<'a>>,
|
||||
returns: Option<ComparableExpr<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
AsyncFunctionDef {
|
||||
name: &'a str,
|
||||
args: ComparableArguments<'a>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
decorator_list: Vec<ComparableExpr<'a>>,
|
||||
returns: Option<ComparableExpr<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
ClassDef {
|
||||
name: &'a str,
|
||||
bases: Vec<ComparableExpr<'a>>,
|
||||
keywords: Vec<ComparableKeyword<'a>>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
decorator_list: Vec<ComparableExpr<'a>>,
|
||||
},
|
||||
Return {
|
||||
value: Option<ComparableExpr<'a>>,
|
||||
},
|
||||
Delete {
|
||||
targets: Vec<ComparableExpr<'a>>,
|
||||
},
|
||||
Assign {
|
||||
targets: Vec<ComparableExpr<'a>>,
|
||||
value: ComparableExpr<'a>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
AugAssign {
|
||||
target: ComparableExpr<'a>,
|
||||
op: ComparableOperator,
|
||||
value: ComparableExpr<'a>,
|
||||
},
|
||||
AnnAssign {
|
||||
target: ComparableExpr<'a>,
|
||||
annotation: ComparableExpr<'a>,
|
||||
value: Option<ComparableExpr<'a>>,
|
||||
simple: usize,
|
||||
},
|
||||
For {
|
||||
target: ComparableExpr<'a>,
|
||||
iter: ComparableExpr<'a>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
AsyncFor {
|
||||
target: ComparableExpr<'a>,
|
||||
iter: ComparableExpr<'a>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
While {
|
||||
test: ComparableExpr<'a>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
},
|
||||
If {
|
||||
test: ComparableExpr<'a>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
},
|
||||
With {
|
||||
items: Vec<ComparableWithitem<'a>>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
AsyncWith {
|
||||
items: Vec<ComparableWithitem<'a>>,
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
Raise {
|
||||
exc: Option<ComparableExpr<'a>>,
|
||||
cause: Option<ComparableExpr<'a>>,
|
||||
},
|
||||
Try {
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
handlers: Vec<ComparableExcepthandler<'a>>,
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
finalbody: Vec<ComparableStmt<'a>>,
|
||||
},
|
||||
Assert {
|
||||
test: ComparableExpr<'a>,
|
||||
msg: Option<ComparableExpr<'a>>,
|
||||
},
|
||||
Import {
|
||||
names: Vec<ComparableAlias<'a>>,
|
||||
},
|
||||
ImportFrom {
|
||||
module: Option<&'a str>,
|
||||
names: Vec<ComparableAlias<'a>>,
|
||||
level: Option<usize>,
|
||||
},
|
||||
Global {
|
||||
names: Vec<&'a str>,
|
||||
},
|
||||
Nonlocal {
|
||||
names: Vec<&'a str>,
|
||||
},
|
||||
Expr {
|
||||
value: ComparableExpr<'a>,
|
||||
},
|
||||
Pass,
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Stmt> for ComparableStmt<'a> {
|
||||
fn from(stmt: &'a Stmt) -> Self {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef {
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
decorator_list,
|
||||
returns,
|
||||
type_comment,
|
||||
} => Self::FunctionDef {
|
||||
name,
|
||||
args: args.into(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
decorator_list: decorator_list.iter().map(Into::into).collect(),
|
||||
returns: returns.as_ref().map(Into::into),
|
||||
type_comment: type_comment.as_ref().map(std::string::String::as_str),
|
||||
},
|
||||
StmtKind::AsyncFunctionDef {
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
decorator_list,
|
||||
returns,
|
||||
type_comment,
|
||||
} => Self::AsyncFunctionDef {
|
||||
name,
|
||||
args: args.into(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
decorator_list: decorator_list.iter().map(Into::into).collect(),
|
||||
returns: returns.as_ref().map(Into::into),
|
||||
type_comment: type_comment.as_ref().map(std::string::String::as_str),
|
||||
},
|
||||
StmtKind::ClassDef {
|
||||
name,
|
||||
bases,
|
||||
keywords,
|
||||
body,
|
||||
decorator_list,
|
||||
} => Self::ClassDef {
|
||||
name,
|
||||
bases: bases.iter().map(Into::into).collect(),
|
||||
keywords: keywords.iter().map(Into::into).collect(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
decorator_list: decorator_list.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::Return { value } => Self::Return {
|
||||
value: value.as_ref().map(Into::into),
|
||||
},
|
||||
StmtKind::Delete { targets } => Self::Delete {
|
||||
targets: targets.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::Assign {
|
||||
targets,
|
||||
value,
|
||||
type_comment,
|
||||
} => Self::Assign {
|
||||
targets: targets.iter().map(Into::into).collect(),
|
||||
value: value.into(),
|
||||
type_comment: type_comment.as_ref().map(std::string::String::as_str),
|
||||
},
|
||||
StmtKind::AugAssign { target, op, value } => Self::AugAssign {
|
||||
target: target.into(),
|
||||
op: op.into(),
|
||||
value: value.into(),
|
||||
},
|
||||
StmtKind::AnnAssign {
|
||||
target,
|
||||
annotation,
|
||||
value,
|
||||
simple,
|
||||
} => Self::AnnAssign {
|
||||
target: target.into(),
|
||||
annotation: annotation.into(),
|
||||
value: value.as_ref().map(Into::into),
|
||||
simple: *simple,
|
||||
},
|
||||
StmtKind::For {
|
||||
target,
|
||||
iter,
|
||||
body,
|
||||
orelse,
|
||||
type_comment,
|
||||
} => Self::For {
|
||||
target: target.into(),
|
||||
iter: iter.into(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
type_comment: type_comment.as_ref().map(String::as_str),
|
||||
},
|
||||
StmtKind::AsyncFor {
|
||||
target,
|
||||
iter,
|
||||
body,
|
||||
orelse,
|
||||
type_comment,
|
||||
} => Self::AsyncFor {
|
||||
target: target.into(),
|
||||
iter: iter.into(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
type_comment: type_comment.as_ref().map(String::as_str),
|
||||
},
|
||||
StmtKind::While { test, body, orelse } => Self::While {
|
||||
test: test.into(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::If { test, body, orelse } => Self::If {
|
||||
test: test.into(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::With {
|
||||
items,
|
||||
body,
|
||||
type_comment,
|
||||
} => Self::With {
|
||||
items: items.iter().map(Into::into).collect(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
type_comment: type_comment.as_ref().map(String::as_str),
|
||||
},
|
||||
StmtKind::AsyncWith {
|
||||
items,
|
||||
body,
|
||||
type_comment,
|
||||
} => Self::AsyncWith {
|
||||
items: items.iter().map(Into::into).collect(),
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
type_comment: type_comment.as_ref().map(String::as_str),
|
||||
},
|
||||
StmtKind::Match { .. } => unreachable!("StmtKind::Match is not supported"),
|
||||
StmtKind::Raise { exc, cause } => Self::Raise {
|
||||
exc: exc.as_ref().map(Into::into),
|
||||
cause: cause.as_ref().map(Into::into),
|
||||
},
|
||||
StmtKind::Try {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => Self::Try {
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
handlers: handlers.iter().map(Into::into).collect(),
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
finalbody: finalbody.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::Assert { test, msg } => Self::Assert {
|
||||
test: test.into(),
|
||||
msg: msg.as_ref().map(Into::into),
|
||||
},
|
||||
StmtKind::Import { names } => Self::Import {
|
||||
names: names.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::ImportFrom {
|
||||
module,
|
||||
names,
|
||||
level,
|
||||
} => Self::ImportFrom {
|
||||
module: module.as_ref().map(String::as_str),
|
||||
names: names.iter().map(Into::into).collect(),
|
||||
level: *level,
|
||||
},
|
||||
StmtKind::Global { names } => Self::Global {
|
||||
names: names.iter().map(String::as_str).collect(),
|
||||
},
|
||||
StmtKind::Nonlocal { names } => Self::Nonlocal {
|
||||
names: names.iter().map(String::as_str).collect(),
|
||||
},
|
||||
StmtKind::Expr { value } => Self::Expr {
|
||||
value: value.into(),
|
||||
},
|
||||
StmtKind::Pass => Self::Pass,
|
||||
StmtKind::Break => Self::Break,
|
||||
StmtKind::Continue => Self::Continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_parser::ast::Expr;
|
||||
|
||||
use crate::ast::helpers::to_call_path;
|
||||
use crate::ast::helpers::{map_callable, to_call_path};
|
||||
use crate::ast::types::{Scope, ScopeKind};
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -29,18 +29,20 @@ pub fn classify(
|
||||
if decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a static method decorator (like
|
||||
// `@staticmethod`).
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
staticmethod_decorators
|
||||
.iter()
|
||||
.any(|decorator| call_path == to_call_path(decorator))
|
||||
})
|
||||
checker
|
||||
.resolve_call_path(map_callable(expr))
|
||||
.map_or(false, |call_path| {
|
||||
staticmethod_decorators
|
||||
.iter()
|
||||
.any(|decorator| call_path == to_call_path(decorator))
|
||||
})
|
||||
}) {
|
||||
FunctionType::StaticMethod
|
||||
} else if CLASS_METHODS.contains(&name)
|
||||
// Special-case class method, like `__new__`.
|
||||
|| scope.bases.iter().any(|expr| {
|
||||
// The class itself extends a known metaclass, so all methods are class methods.
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
|
||||
METACLASS_BASES
|
||||
.iter()
|
||||
.any(|(module, member)| call_path.as_slice() == [*module, *member])
|
||||
@@ -48,7 +50,7 @@ pub fn classify(
|
||||
})
|
||||
|| decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
|
||||
classmethod_decorators
|
||||
.iter()
|
||||
.any(|decorator| call_path == to_call_path(decorator))
|
||||
|
||||
@@ -568,6 +568,17 @@ pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
|
||||
arg_names
|
||||
}
|
||||
|
||||
/// Given an [`Expr`] that can be callable or not (like a decorator, which could
|
||||
/// be used with or without explicit call syntax), return the underlying
|
||||
/// callable.
|
||||
pub fn map_callable(decorator: &Expr) -> &Expr {
|
||||
if let ExprKind::Call { func, .. } = &decorator.node {
|
||||
func
|
||||
} else {
|
||||
decorator
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if a statement or expression includes at least one comment.
|
||||
pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
|
||||
let start = if match_leading_content(located, locator) {
|
||||
@@ -585,7 +596,7 @@ pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
|
||||
|
||||
/// Returns `true` if a [`Range`] includes at least one comment.
|
||||
pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
|
||||
for tok in lexer::make_tokenizer(locator.slice_source_code_range(&range)) {
|
||||
for tok in lexer::make_tokenizer(locator.slice(&range)) {
|
||||
match tok {
|
||||
Ok((_, tok, _)) => {
|
||||
if matches!(tok, Tok::Comment(..)) {
|
||||
@@ -712,7 +723,7 @@ where
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
// Don't recurse.
|
||||
}
|
||||
StmtKind::Return { value } => self.returns.push(value.as_ref().map(|expr| &**expr)),
|
||||
StmtKind::Return { value } => self.returns.push(value.as_deref()),
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
@@ -745,7 +756,7 @@ pub fn to_relative(absolute: Location, base: Location) -> Location {
|
||||
/// Return `true` if a [`Located`] has leading content.
|
||||
pub fn match_leading_content<T>(located: &Located<T>, locator: &Locator) -> bool {
|
||||
let range = Range::new(Location::new(located.location.row(), 0), located.location);
|
||||
let prefix = locator.slice_source_code_range(&range);
|
||||
let prefix = locator.slice(&range);
|
||||
prefix.chars().any(|char| !char.is_whitespace())
|
||||
}
|
||||
|
||||
@@ -755,7 +766,7 @@ pub fn match_trailing_content<T>(located: &Located<T>, locator: &Locator) -> boo
|
||||
located.end_location.unwrap(),
|
||||
Location::new(located.end_location.unwrap().row() + 1, 0),
|
||||
);
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
let suffix = locator.slice(&range);
|
||||
for char in suffix.chars() {
|
||||
if char == '#' {
|
||||
return false;
|
||||
@@ -773,7 +784,7 @@ pub fn match_trailing_comment<T>(located: &Located<T>, locator: &Locator) -> Opt
|
||||
located.end_location.unwrap(),
|
||||
Location::new(located.end_location.unwrap().row() + 1, 0),
|
||||
);
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
let suffix = locator.slice(&range);
|
||||
for (i, char) in suffix.chars().enumerate() {
|
||||
if char == '#' {
|
||||
return Some(i);
|
||||
@@ -787,8 +798,7 @@ pub fn match_trailing_comment<T>(located: &Located<T>, locator: &Locator) -> Opt
|
||||
|
||||
/// Return the number of trailing empty lines following a statement.
|
||||
pub fn count_trailing_lines(stmt: &Stmt, locator: &Locator) -> usize {
|
||||
let suffix =
|
||||
locator.slice_source_code_at(Location::new(stmt.end_location.unwrap().row() + 1, 0));
|
||||
let suffix = locator.skip(Location::new(stmt.end_location.unwrap().row() + 1, 0));
|
||||
suffix
|
||||
.lines()
|
||||
.take_while(|line| line.trim().is_empty())
|
||||
@@ -797,7 +807,7 @@ pub fn count_trailing_lines(stmt: &Stmt, locator: &Locator) -> usize {
|
||||
|
||||
/// Return the range of the first parenthesis pair after a given [`Location`].
|
||||
pub fn match_parens(start: Location, locator: &Locator) -> Option<Range> {
|
||||
let contents = locator.slice_source_code_at(start);
|
||||
let contents = locator.skip(start);
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
let mut count: usize = 0;
|
||||
@@ -832,7 +842,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
|
||||
| StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
) {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let contents = locator.slice(&Range::from_located(stmt));
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt.location).flatten() {
|
||||
if matches!(tok, Tok::Name { .. }) {
|
||||
return Range::new(start, end);
|
||||
@@ -860,7 +870,7 @@ pub fn binding_range(binding: &Binding, locator: &Locator) -> Range {
|
||||
|
||||
// Return the ranges of `Name` tokens within a specified node.
|
||||
pub fn find_names<T>(located: &Located<T>, locator: &Locator) -> Vec<Range> {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(located));
|
||||
let contents = locator.slice(&Range::from_located(located));
|
||||
lexer::make_tokenizer_located(contents, located.location)
|
||||
.flatten()
|
||||
.filter(|(_, tok, _)| matches!(tok, Tok::Name { .. }))
|
||||
@@ -879,8 +889,7 @@ pub fn excepthandler_name_range(handler: &Excepthandler, locator: &Locator) -> O
|
||||
match (name, type_) {
|
||||
(Some(_), Some(type_)) => {
|
||||
let type_end_location = type_.end_location.unwrap();
|
||||
let contents =
|
||||
locator.slice_source_code_range(&Range::new(type_end_location, body[0].location));
|
||||
let contents = locator.slice(&Range::new(type_end_location, body[0].location));
|
||||
let range = lexer::make_tokenizer_located(contents, type_end_location)
|
||||
.flatten()
|
||||
.tuple_windows()
|
||||
@@ -904,7 +913,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||
.expect("Expected body to be non-empty")
|
||||
.location
|
||||
};
|
||||
let contents = locator.slice_source_code_range(&Range {
|
||||
let contents = locator.slice(&Range {
|
||||
location: handler.location,
|
||||
end_location: end,
|
||||
});
|
||||
@@ -921,7 +930,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||
|
||||
/// Find f-strings that don't contain any formatted values in a `JoinedStr`.
|
||||
pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
let contents = locator.slice(&Range::from_located(expr));
|
||||
lexer::make_tokenizer_located(contents, expr.location)
|
||||
.flatten()
|
||||
.filter_map(|(location, tok, end_location)| match tok {
|
||||
@@ -929,7 +938,7 @@ pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Ran
|
||||
kind: StringKind::FString | StringKind::RawFString,
|
||||
..
|
||||
} => {
|
||||
let first_char = locator.slice_source_code_range(&Range {
|
||||
let first_char = locator.slice(&Range {
|
||||
location,
|
||||
end_location: Location::new(location.row(), location.column() + 1),
|
||||
});
|
||||
@@ -969,7 +978,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
.expect("Expected body to be non-empty")
|
||||
.end_location
|
||||
.unwrap();
|
||||
let contents = locator.slice_source_code_range(&Range {
|
||||
let contents = locator.slice(&Range {
|
||||
location: body_end,
|
||||
end_location: orelse
|
||||
.first()
|
||||
@@ -991,7 +1000,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
|
||||
/// Return the `Range` of the first `Tok::Colon` token in a `Range`.
|
||||
pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
|
||||
let contents = locator.slice_source_code_range(&range);
|
||||
let contents = locator.slice(&range);
|
||||
let range = lexer::make_tokenizer_located(contents, range.location)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Colon))
|
||||
@@ -1021,7 +1030,7 @@ pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
[stmt, ..] => stmt.location,
|
||||
_ => return None,
|
||||
};
|
||||
let contents = locator.slice_source_code_range(&Range::new(start, end));
|
||||
let contents = locator.slice(&Range::new(start, end));
|
||||
let range = lexer::make_tokenizer_located(contents, start)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Elif | Tok::Else))
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::source_code::Locator;
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<&'a str> {
|
||||
let range = Range::from_located(located);
|
||||
let indentation = locator.slice_source_code_range(&Range::new(
|
||||
let indentation = locator.slice(&Range::new(
|
||||
Location::new(range.location.row(), 0),
|
||||
Location::new(range.location.row(), range.location.column()),
|
||||
));
|
||||
|
||||
@@ -81,7 +81,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
/// Return the location of a trailing semicolon following a `Stmt`, if it's part
|
||||
/// of a multi-statement line.
|
||||
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
let contents = locator.slice_source_code_at(stmt.end_location.unwrap());
|
||||
let contents = locator.skip(stmt.end_location.unwrap());
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with(';') {
|
||||
@@ -104,7 +104,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
/// Find the next valid break for a `Stmt` after a semicolon.
|
||||
fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
|
||||
let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
|
||||
let contents = locator.slice_source_code_at(start_location);
|
||||
let contents = locator.skip(start_location);
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
// Skip past any continuations.
|
||||
@@ -136,7 +136,7 @@ fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
|
||||
|
||||
/// Return `true` if a `Stmt` occurs at the end of a file.
|
||||
fn is_end_of_file(stmt: &Stmt, locator: &Locator) -> bool {
|
||||
let contents = locator.slice_source_code_at(stmt.end_location.unwrap());
|
||||
let contents = locator.skip(stmt.end_location.unwrap());
|
||||
contents.is_empty()
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ pub fn remove_unused_imports<'a>(
|
||||
indexer: &Indexer,
|
||||
stylist: &Stylist,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let module_text = locator.slice(&Range::from_located(stmt));
|
||||
let mut tree = match_module(module_text)?;
|
||||
|
||||
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
|
||||
@@ -339,7 +339,7 @@ pub fn remove_argument(
|
||||
remove_parentheses: bool,
|
||||
) -> Result<Fix> {
|
||||
// TODO(sbrugman): Preserve trailing comments.
|
||||
let contents = locator.slice_source_code_at(stmt_at);
|
||||
let contents = locator.skip(stmt_at);
|
||||
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
|
||||
@@ -1,42 +1,49 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::linter::FixTable;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::Locator;
|
||||
|
||||
pub mod helpers;
|
||||
|
||||
/// Auto-fix errors in a file, and write the fixed source code to disk.
|
||||
pub fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option<(String, usize)> {
|
||||
pub fn fix_file(diagnostics: &[Diagnostic], locator: &Locator) -> Option<(String, FixTable)> {
|
||||
if diagnostics.iter().all(|check| check.fix.is_none()) {
|
||||
return None;
|
||||
None
|
||||
} else {
|
||||
Some(apply_fixes(diagnostics.iter(), locator))
|
||||
}
|
||||
|
||||
Some(apply_fixes(
|
||||
diagnostics.iter().filter_map(|check| check.fix.as_ref()),
|
||||
locator,
|
||||
))
|
||||
}
|
||||
|
||||
/// Apply a series of fixes.
|
||||
fn apply_fixes<'a>(
|
||||
fixes: impl Iterator<Item = &'a Fix>,
|
||||
diagnostics: impl Iterator<Item = &'a Diagnostic>,
|
||||
locator: &'a Locator<'a>,
|
||||
) -> (String, usize) {
|
||||
) -> (String, FixTable) {
|
||||
let mut output = String::with_capacity(locator.len());
|
||||
let mut last_pos: Location = Location::new(1, 0);
|
||||
let mut applied: BTreeSet<&Fix> = BTreeSet::default();
|
||||
let mut num_fixed: usize = 0;
|
||||
let mut fixed = FxHashMap::default();
|
||||
|
||||
for fix in fixes.sorted_by_key(|fix| fix.location) {
|
||||
for (rule, fix) in diagnostics
|
||||
.filter_map(|diagnostic| {
|
||||
diagnostic
|
||||
.fix
|
||||
.as_ref()
|
||||
.map(|fix| (diagnostic.kind.rule(), fix))
|
||||
})
|
||||
.sorted_by_key(|(.., fix)| fix.location)
|
||||
{
|
||||
// If we already applied an identical fix as part of another correction, skip
|
||||
// any re-application.
|
||||
if applied.contains(&fix) {
|
||||
num_fixed += 1;
|
||||
*fixed.entry(rule).or_default() += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ fn apply_fixes<'a>(
|
||||
}
|
||||
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice_source_code_range(&Range::new(last_pos, fix.location));
|
||||
let slice = locator.slice(&Range::new(last_pos, fix.location));
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the patch itself.
|
||||
@@ -56,14 +63,14 @@ fn apply_fixes<'a>(
|
||||
// Track that the fix was applied.
|
||||
last_pos = fix.end_location;
|
||||
applied.insert(fix);
|
||||
num_fixed += 1;
|
||||
*fixed.entry(rule).or_default() += 1;
|
||||
}
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = locator.slice_source_code_at(last_pos);
|
||||
let slice = locator.skip(last_pos);
|
||||
output.push_str(slice);
|
||||
|
||||
(output, num_fixed)
|
||||
(output, fixed)
|
||||
}
|
||||
|
||||
/// Apply a single fix.
|
||||
@@ -71,14 +78,14 @@ pub(crate) fn apply_fix(fix: &Fix, locator: &Locator) -> String {
|
||||
let mut output = String::with_capacity(locator.len());
|
||||
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice_source_code_range(&Range::new(Location::new(1, 0), fix.location));
|
||||
let slice = locator.slice(&Range::new(Location::new(1, 0), fix.location));
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the patch itself.
|
||||
output.push_str(&fix.content);
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = locator.slice_source_code_at(fix.end_location);
|
||||
let slice = locator.skip(fix.end_location);
|
||||
output.push_str(slice);
|
||||
|
||||
output
|
||||
@@ -90,24 +97,41 @@ mod tests {
|
||||
|
||||
use crate::autofix::{apply_fix, apply_fixes};
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::rules::pycodestyle::rules::NoNewLineAtEndOfFile;
|
||||
|
||||
use crate::source_code::Locator;
|
||||
|
||||
#[test]
|
||||
fn empty_file() {
|
||||
let fixes = vec![];
|
||||
let fixes: Vec<Diagnostic> = vec![];
|
||||
let locator = Locator::new(r#""#);
|
||||
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
|
||||
assert_eq!(contents, "");
|
||||
assert_eq!(fixed, 0);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 0);
|
||||
}
|
||||
|
||||
impl From<Fix> for Diagnostic {
|
||||
fn from(fix: Fix) -> Self {
|
||||
Diagnostic {
|
||||
// The choice of rule here is arbitrary.
|
||||
kind: NoNewLineAtEndOfFile.into(),
|
||||
location: fix.location,
|
||||
end_location: fix.end_location,
|
||||
fix: Some(fix),
|
||||
parent: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_one_replacement() {
|
||||
let fixes = vec![Fix {
|
||||
let fixes: Vec<Diagnostic> = vec![Fix {
|
||||
content: "Bar".to_string(),
|
||||
location: Location::new(1, 8),
|
||||
end_location: Location::new(1, 14),
|
||||
}];
|
||||
}
|
||||
.into()];
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
class A(object):
|
||||
@@ -124,16 +148,17 @@ class A(Bar):
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixed, 1);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_one_removal() {
|
||||
let fixes = vec![Fix {
|
||||
let fixes: Vec<Diagnostic> = vec![Fix {
|
||||
content: String::new(),
|
||||
location: Location::new(1, 7),
|
||||
end_location: Location::new(1, 15),
|
||||
}];
|
||||
}
|
||||
.into()];
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
class A(object):
|
||||
@@ -150,22 +175,24 @@ class A:
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixed, 1);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_two_removals() {
|
||||
let fixes = vec![
|
||||
let fixes: Vec<Diagnostic> = vec![
|
||||
Fix {
|
||||
content: String::new(),
|
||||
location: Location::new(1, 7),
|
||||
end_location: Location::new(1, 16),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
Fix {
|
||||
content: String::new(),
|
||||
location: Location::new(1, 16),
|
||||
end_location: Location::new(1, 23),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
];
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
@@ -184,22 +211,24 @@ class A:
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixed, 2);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_overlapping_fixes() {
|
||||
let fixes = vec![
|
||||
let fixes: Vec<Diagnostic> = vec![
|
||||
Fix {
|
||||
content: String::new(),
|
||||
location: Location::new(1, 7),
|
||||
end_location: Location::new(1, 15),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
Fix {
|
||||
content: "ignored".to_string(),
|
||||
location: Location::new(1, 9),
|
||||
end_location: Location::new(1, 11),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
];
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
@@ -217,7 +246,7 @@ class A:
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixed, 1);
|
||||
assert_eq!(fixed.values().sum::<usize>(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -6,8 +6,6 @@ use std::path::Path;
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use nohash_hasher::IntMap;
|
||||
use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_python::typing::TYPING_EXTENSIONS;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_common::cformat::{CFormatError, CFormatErrorType};
|
||||
use rustpython_parser::ast::{
|
||||
@@ -17,6 +15,9 @@ use rustpython_parser::ast::{
|
||||
use rustpython_parser::parser;
|
||||
use smallvec::smallvec;
|
||||
|
||||
use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_python::typing::TYPING_EXTENSIONS;
|
||||
|
||||
use crate::ast::helpers::{
|
||||
binding_range, collect_call_path, extract_handler_names, from_relative_import, to_module_path,
|
||||
};
|
||||
@@ -30,16 +31,15 @@ use crate::ast::typing::{match_annotated_subscript, Callable, SubscriptKind};
|
||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||
use crate::ast::{branch_detection, cast, helpers, operations, typing, visitor};
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable};
|
||||
use crate::noqa::Directive;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::{
|
||||
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
|
||||
flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger,
|
||||
flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_logging_format,
|
||||
flake8_pie, flake8_print, flake8_pytest_style, flake8_raise, flake8_return, flake8_self,
|
||||
flake8_simplify, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments,
|
||||
flake8_use_pathlib, mccabe, pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes,
|
||||
pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
flake8_django, flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_logging_format, flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_raise,
|
||||
flake8_return, flake8_self, flake8_simplify, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, flake8_use_pathlib, mccabe, numpy, pandas_vet, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
};
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -50,6 +50,7 @@ use crate::{autofix, docstrings, noqa, visibility};
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
|
||||
type DeferralContext<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
|
||||
type AnnotationContext = (bool, bool);
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Checker<'a> {
|
||||
@@ -84,8 +85,8 @@ pub struct Checker<'a> {
|
||||
pub(crate) scopes: Vec<Scope<'a>>,
|
||||
pub(crate) scope_stack: Vec<usize>,
|
||||
pub(crate) dead_scopes: Vec<(usize, Vec<usize>)>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext<'a>)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext<'a>)>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, AnnotationContext, DeferralContext<'a>)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, AnnotationContext, DeferralContext<'a>)>,
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
||||
deferred_for_loops: Vec<(&'a Stmt, DeferralContext<'a>)>,
|
||||
@@ -282,7 +283,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
/// Return `true` if a `Rule` is disabled by a `noqa` directive.
|
||||
pub fn is_ignored(&self, code: &Rule, lineno: usize) -> bool {
|
||||
pub fn rule_is_ignored(&self, code: &Rule, lineno: usize) -> bool {
|
||||
// TODO(charlie): `noqa` directives are mostly enforced in `check_lines.rs`.
|
||||
// However, in rare cases, we need to check them here. For example, when
|
||||
// removing unused imports, we create a single fix that's applied to all
|
||||
@@ -293,16 +294,7 @@ impl<'a> Checker<'a> {
|
||||
if matches!(self.noqa, flags::Noqa::Disabled) {
|
||||
return false;
|
||||
}
|
||||
let noqa_lineno = self.noqa_line_for.get(&lineno).unwrap_or(&lineno);
|
||||
let line = self.locator.slice_source_code_range(&Range::new(
|
||||
Location::new(*noqa_lineno, 0),
|
||||
Location::new(noqa_lineno + 1, 0),
|
||||
));
|
||||
match noqa::extract_noqa_directive(line) {
|
||||
Directive::None => false,
|
||||
Directive::All(..) => true,
|
||||
Directive::Codes(.., codes) => noqa::includes(code, &codes),
|
||||
}
|
||||
noqa::rule_is_ignored(code, lineno, self.noqa_line_for, self.locator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,6 +465,17 @@ where
|
||||
body,
|
||||
..
|
||||
} => {
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::NonLeadingReceiverDecorator)
|
||||
{
|
||||
self.diagnostics
|
||||
.extend(flake8_django::rules::non_leading_receiver_decorator(
|
||||
decorator_list,
|
||||
|expr| self.resolve_call_path(expr),
|
||||
));
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AmbiguousFunctionName) {
|
||||
if let Some(diagnostic) =
|
||||
pycodestyle::rules::ambiguous_function_name(name, || {
|
||||
@@ -575,7 +578,7 @@ where
|
||||
flake8_return::rules::function(self, body);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::FunctionIsTooComplex) {
|
||||
if self.settings.rules.enabled(&Rule::ComplexStructure) {
|
||||
if let Some(diagnostic) = mccabe::rules::function_is_too_complex(
|
||||
stmt,
|
||||
name,
|
||||
@@ -780,6 +783,19 @@ where
|
||||
decorator_list,
|
||||
body,
|
||||
} => {
|
||||
if self.settings.rules.enabled(&Rule::NullableModelStringField) {
|
||||
self.diagnostics
|
||||
.extend(flake8_django::rules::nullable_model_string_field(
|
||||
self, body,
|
||||
));
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ModelWithoutDunderStr) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_django::rules::model_without_dunder_str(self, bases, body, stmt)
|
||||
{
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UselessObjectInheritance) {
|
||||
pyupgrade::rules::useless_object_inheritance(self, stmt, name, bases, keywords);
|
||||
}
|
||||
@@ -1235,9 +1251,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::ImportStarUsed) {
|
||||
if self.settings.rules.enabled(&Rule::ImportStar) {
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::ImportStarUsed {
|
||||
pyflakes::rules::ImportStar {
|
||||
name: helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
@@ -1300,8 +1316,11 @@ where
|
||||
if self.settings.rules.enabled(&Rule::RelativeImports) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_tidy_imports::relative_imports::banned_relative_import(
|
||||
self,
|
||||
stmt,
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
self.module_path.as_ref(),
|
||||
&self.settings.flake8_tidy_imports.ban_relative_imports,
|
||||
)
|
||||
{
|
||||
@@ -1486,7 +1505,7 @@ where
|
||||
if self.settings.rules.enabled(&Rule::IfTuple) {
|
||||
pyflakes::rules::if_tuple(self, stmt, test);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NestedIfStatements) {
|
||||
if self.settings.rules.enabled(&Rule::CollapsibleIf) {
|
||||
flake8_simplify::rules::nested_if_statements(
|
||||
self,
|
||||
stmt,
|
||||
@@ -1496,11 +1515,14 @@ where
|
||||
self.current_stmt_parent().map(Into::into),
|
||||
);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::ReturnBoolConditionDirectly)
|
||||
{
|
||||
if self.settings.rules.enabled(&Rule::IfWithSameArms) {
|
||||
flake8_simplify::rules::if_with_same_arms(
|
||||
self,
|
||||
stmt,
|
||||
self.current_stmt_parent().map(std::convert::Into::into),
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NeedlessBool) {
|
||||
flake8_simplify::rules::return_bool_condition_directly(self, stmt);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UseTernaryOperator) {
|
||||
@@ -1537,15 +1559,10 @@ where
|
||||
if self.settings.rules.enabled(&Rule::AssertTuple) {
|
||||
pyflakes::rules::assert_tuple(self, stmt, test);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::DoNotAssertFalse) {
|
||||
flake8_bugbear::rules::assert_false(
|
||||
self,
|
||||
stmt,
|
||||
test,
|
||||
msg.as_ref().map(|expr| &**expr),
|
||||
);
|
||||
if self.settings.rules.enabled(&Rule::AssertFalse) {
|
||||
flake8_bugbear::rules::assert_false(self, stmt, test, msg.as_deref());
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AssertUsed) {
|
||||
if self.settings.rules.enabled(&Rule::Assert) {
|
||||
self.diagnostics
|
||||
.push(flake8_bandit::rules::assert_used(stmt));
|
||||
}
|
||||
@@ -1704,9 +1721,9 @@ where
|
||||
}
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
if self.settings.rules.enabled(&Rule::DoNotAssignLambda) {
|
||||
if self.settings.rules.enabled(&Rule::LambdaAssignment) {
|
||||
if let [target] = &targets[..] {
|
||||
pycodestyle::rules::do_not_assign_lambda(self, target, value, stmt);
|
||||
pycodestyle::rules::lambda_assignment(self, target, value, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1722,6 +1739,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::PrefixTypeParams) {
|
||||
if self.path.extension().map_or(false, |ext| ext == "pyi") {
|
||||
flake8_pyi::rules::prefix_type_params(self, value, targets);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::UselessMetaclassType) {
|
||||
pyupgrade::rules::useless_metaclass_type(self, stmt, value, targets);
|
||||
}
|
||||
@@ -1754,9 +1777,9 @@ where
|
||||
}
|
||||
}
|
||||
StmtKind::AnnAssign { target, value, .. } => {
|
||||
if self.settings.rules.enabled(&Rule::DoNotAssignLambda) {
|
||||
if self.settings.rules.enabled(&Rule::LambdaAssignment) {
|
||||
if let Some(value) = value {
|
||||
pycodestyle::rules::do_not_assign_lambda(self, target, value, stmt);
|
||||
pycodestyle::rules::lambda_assignment(self, target, value, stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1772,6 +1795,13 @@ where
|
||||
{
|
||||
flake8_simplify::rules::use_capital_environment_variables(self, value);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AsyncioDanglingTask) {
|
||||
if let Some(diagnostic) = ruff::rules::asyncio_dangling_task(value, |expr| {
|
||||
self.resolve_call_path(expr)
|
||||
}) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -2042,13 +2072,13 @@ where
|
||||
self.deferred_string_type_definitions.push((
|
||||
Range::from_located(expr),
|
||||
value,
|
||||
self.in_annotation,
|
||||
(self.in_annotation, self.in_type_checking_block),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
} else {
|
||||
self.deferred_type_definitions.push((
|
||||
expr,
|
||||
self.in_annotation,
|
||||
(self.in_annotation, self.in_type_checking_block),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
}
|
||||
@@ -2066,7 +2096,7 @@ where
|
||||
// Ex) Optional[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.settings.rules.enabled(&Rule::UsePEP604Annotation)
|
||||
&& self.settings.rules.enabled(&Rule::TypingUnion)
|
||||
&& (self.settings.target_version >= PythonVersion::Py310
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& self.annotations_future_enabled
|
||||
@@ -2117,11 +2147,14 @@ where
|
||||
if self.settings.rules.enabled(&Rule::TypingTextStrAlias) {
|
||||
pyupgrade::rules::typing_text_str_alias(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NumpyDeprecatedTypeAlias) {
|
||||
numpy::rules::deprecated_type_alias(self, expr);
|
||||
}
|
||||
|
||||
// Ex) List[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.settings.rules.enabled(&Rule::UsePEP585Annotation)
|
||||
&& self.settings.rules.enabled(&Rule::DeprecatedCollectionType)
|
||||
&& (self.settings.target_version >= PythonVersion::Py39
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& self.annotations_future_enabled
|
||||
@@ -2166,7 +2199,7 @@ where
|
||||
// Ex) typing.List[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.settings.rules.enabled(&Rule::UsePEP585Annotation)
|
||||
&& self.settings.rules.enabled(&Rule::DeprecatedCollectionType)
|
||||
&& (self.settings.target_version >= PythonVersion::Py39
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& self.annotations_future_enabled
|
||||
@@ -2183,6 +2216,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::TypingTextStrAlias) {
|
||||
pyupgrade::rules::typing_text_str_alias(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NumpyDeprecatedTypeAlias) {
|
||||
numpy::rules::deprecated_type_alias(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RewriteMockImport) {
|
||||
pyupgrade::rules::rewrite_mock_attribute(self, expr);
|
||||
}
|
||||
@@ -2367,12 +2403,12 @@ where
|
||||
}
|
||||
|
||||
// flake8-pie
|
||||
if self.settings.rules.enabled(&Rule::NoUnnecessaryDictKwargs) {
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryDictKwargs) {
|
||||
flake8_pie::rules::no_unnecessary_dict_kwargs(self, expr, keywords);
|
||||
}
|
||||
|
||||
// flake8-bandit
|
||||
if self.settings.rules.enabled(&Rule::ExecUsed) {
|
||||
if self.settings.rules.enabled(&Rule::ExecBuiltin) {
|
||||
if let Some(diagnostic) = flake8_bandit::rules::exec_used(expr, func) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -2405,6 +2441,9 @@ where
|
||||
self.diagnostics
|
||||
.extend(flake8_bandit::rules::hardcoded_password_func_arg(keywords));
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::HardcodedSQLExpression) {
|
||||
flake8_bandit::rules::hardcoded_sql_expression(self, expr);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
@@ -2435,12 +2474,22 @@ where
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryGeneratorSet) {
|
||||
flake8_comprehensions::rules::unnecessary_generator_set(
|
||||
self, expr, func, args, keywords,
|
||||
self,
|
||||
expr,
|
||||
self.current_expr_parent().map(Into::into),
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryGeneratorDict) {
|
||||
flake8_comprehensions::rules::unnecessary_generator_dict(
|
||||
self, expr, func, args, keywords,
|
||||
self,
|
||||
expr,
|
||||
self.current_expr_parent().map(Into::into),
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
);
|
||||
}
|
||||
if self
|
||||
@@ -2529,7 +2578,13 @@ where
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryMap) {
|
||||
flake8_comprehensions::rules::unnecessary_map(self, expr, func, args);
|
||||
flake8_comprehensions::rules::unnecessary_map(
|
||||
self,
|
||||
expr,
|
||||
self.current_expr_parent().map(Into::into),
|
||||
func,
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
// flake8-boolean-trap
|
||||
@@ -2782,7 +2837,7 @@ where
|
||||
pyflakes::rules::repeated_keys(self, keys, values);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::NoUnnecessarySpread) {
|
||||
if self.settings.rules.enabled(&Rule::UnnecessarySpread) {
|
||||
flake8_pie::rules::no_unnecessary_spread(self, keys, values);
|
||||
}
|
||||
}
|
||||
@@ -2790,11 +2845,17 @@ where
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
pyflakes::rules::yield_outside_function(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::YieldInInit) {
|
||||
pylint::rules::yield_in_init(self, expr);
|
||||
}
|
||||
}
|
||||
ExprKind::YieldFrom { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
pyflakes::rules::yield_outside_function(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::YieldInInit) {
|
||||
pylint::rules::yield_in_init(self, expr);
|
||||
}
|
||||
}
|
||||
ExprKind::Await { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::YieldOutsideFunction) {
|
||||
@@ -2812,6 +2873,9 @@ where
|
||||
{
|
||||
pyflakes::rules::f_string_missing_placeholders(expr, values, self);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::HardcodedSQLExpression) {
|
||||
flake8_bandit::rules::hardcoded_sql_expression(self, expr);
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
left,
|
||||
@@ -2973,6 +3037,12 @@ where
|
||||
if self.settings.rules.enabled(&Rule::PrintfStringFormatting) {
|
||||
pyupgrade::rules::printf_string_formatting(self, expr, left, right);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::BadStringFormatType) {
|
||||
pylint::rules::bad_string_format_type(self, expr, right);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::HardcodedSQLExpression) {
|
||||
flake8_bandit::rules::hardcoded_sql_expression(self, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
@@ -2994,6 +3064,9 @@ where
|
||||
{
|
||||
ruff::rules::unpack_instead_of_concatenating_to_collection_literal(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::HardcodedSQLExpression) {
|
||||
flake8_bandit::rules::hardcoded_sql_expression(self, expr);
|
||||
}
|
||||
}
|
||||
ExprKind::UnaryOp { op, operand } => {
|
||||
let check_not_in = self.settings.rules.enabled(&Rule::NotInTest);
|
||||
@@ -3100,6 +3173,23 @@ where
|
||||
if self.settings.rules.enabled(&Rule::YodaConditions) {
|
||||
flake8_simplify::rules::yoda_conditions(self, expr, left, ops, comparators);
|
||||
}
|
||||
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UnrecognizedPlatformCheck)
|
||||
|| self.settings.rules.enabled(&Rule::UnrecognizedPlatformName)
|
||||
{
|
||||
if self.path.extension().map_or(false, |ext| ext == "pyi") {
|
||||
flake8_pyi::rules::unrecognized_platform(
|
||||
self,
|
||||
expr,
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
@@ -3109,7 +3199,7 @@ where
|
||||
self.deferred_string_type_definitions.push((
|
||||
Range::from_located(expr),
|
||||
value,
|
||||
self.in_annotation,
|
||||
(self.in_annotation, self.in_type_checking_block),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
}
|
||||
@@ -3137,9 +3227,6 @@ where
|
||||
if self.settings.rules.enabled(&Rule::RewriteUnicodeLiteral) {
|
||||
pyupgrade::rules::rewrite_unicode_literal(self, expr, kind.as_deref());
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::BidirectionalUnicode) {
|
||||
pylint::rules::bidirectional_unicode(self, expr, value);
|
||||
}
|
||||
}
|
||||
ExprKind::Lambda { args, body, .. } => {
|
||||
if self.settings.rules.enabled(&Rule::PreferListBuiltin) {
|
||||
@@ -3521,8 +3608,8 @@ where
|
||||
ExcepthandlerKind::ExceptHandler {
|
||||
type_, name, body, ..
|
||||
} => {
|
||||
if self.settings.rules.enabled(&Rule::DoNotUseBareExcept) {
|
||||
if let Some(diagnostic) = pycodestyle::rules::do_not_use_bare_except(
|
||||
if self.settings.rules.enabled(&Rule::BareExcept) {
|
||||
if let Some(diagnostic) = pycodestyle::rules::bare_except(
|
||||
type_.as_deref(),
|
||||
body,
|
||||
excepthandler,
|
||||
@@ -3549,6 +3636,17 @@ where
|
||||
if self.settings.rules.enabled(&Rule::TryExceptPass) {
|
||||
flake8_bandit::rules::try_except_pass(
|
||||
self,
|
||||
excepthandler,
|
||||
type_.as_deref(),
|
||||
name.as_deref(),
|
||||
body,
|
||||
self.settings.flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::TryExceptContinue) {
|
||||
flake8_bandit::rules::try_except_continue(
|
||||
self,
|
||||
excepthandler,
|
||||
type_.as_deref(),
|
||||
name.as_deref(),
|
||||
body,
|
||||
@@ -3742,7 +3840,7 @@ where
|
||||
}
|
||||
|
||||
fn visit_body(&mut self, body: &'b [Stmt]) {
|
||||
if self.settings.rules.enabled(&Rule::NoUnnecessaryPass) {
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryPass) {
|
||||
flake8_pie::rules::no_unnecessary_pass(self, body);
|
||||
}
|
||||
|
||||
@@ -3855,6 +3953,14 @@ impl<'a> Checker<'a> {
|
||||
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
|
||||
}
|
||||
|
||||
pub fn current_scope_parent(&self) -> Option<&Scope> {
|
||||
self.scope_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.nth(1)
|
||||
.map(|index| &self.scopes[*index])
|
||||
}
|
||||
|
||||
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
|
||||
self.scope_stack
|
||||
.iter()
|
||||
@@ -3870,7 +3976,6 @@ impl<'a> Checker<'a> {
|
||||
if self.in_type_checking_block
|
||||
|| self.in_annotation
|
||||
|| self.in_deferred_string_type_definition
|
||||
|| self.in_deferred_type_definition
|
||||
{
|
||||
ExecutionContext::Typing
|
||||
} else {
|
||||
@@ -4304,16 +4409,18 @@ impl<'a> Checker<'a> {
|
||||
} {
|
||||
let (all_names, all_names_flags) = extract_all_names(self, parent, current);
|
||||
|
||||
if self.settings.rules.enabled(&Rule::InvalidAllFormat)
|
||||
&& matches!(all_names_flags, AllNamesFlags::INVALID_FORMAT)
|
||||
{
|
||||
pylint::rules::invalid_all_format(self, expr);
|
||||
if self.settings.rules.enabled(&Rule::InvalidAllFormat) {
|
||||
if matches!(all_names_flags, AllNamesFlags::INVALID_FORMAT) {
|
||||
self.diagnostics
|
||||
.push(pylint::rules::invalid_all_format(expr));
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::InvalidAllObject)
|
||||
&& matches!(all_names_flags, AllNamesFlags::INVALID_OBJECT)
|
||||
{
|
||||
pylint::rules::invalid_all_object(self, expr);
|
||||
if self.settings.rules.enabled(&Rule::InvalidAllObject) {
|
||||
if matches!(all_names_flags, AllNamesFlags::INVALID_OBJECT) {
|
||||
self.diagnostics
|
||||
.push(pylint::rules::invalid_all_object(expr));
|
||||
}
|
||||
}
|
||||
|
||||
self.add_binding(
|
||||
@@ -4397,12 +4504,13 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn check_deferred_type_definitions(&mut self) {
|
||||
self.deferred_type_definitions.reverse();
|
||||
while let Some((expr, in_annotation, (scopes, parents))) =
|
||||
while let Some((expr, (in_annotation, in_type_checking_block), (scopes, parents))) =
|
||||
self.deferred_type_definitions.pop()
|
||||
{
|
||||
self.scope_stack = scopes;
|
||||
self.parents = parents;
|
||||
self.in_annotation = in_annotation;
|
||||
self.in_type_checking_block = in_type_checking_block;
|
||||
self.in_type_definition = true;
|
||||
self.in_deferred_type_definition = true;
|
||||
self.visit_expr(expr);
|
||||
@@ -4417,7 +4525,7 @@ impl<'a> Checker<'a> {
|
||||
{
|
||||
let mut stacks = vec![];
|
||||
self.deferred_string_type_definitions.reverse();
|
||||
while let Some((range, expression, in_annotation, context)) =
|
||||
while let Some((range, expression, (in_annotation, in_type_checking_block), deferral)) =
|
||||
self.deferred_string_type_definitions.pop()
|
||||
{
|
||||
if let Ok(mut expr) = parser::parse_expression(expression, "<filename>") {
|
||||
@@ -4428,7 +4536,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
relocate_expr(&mut expr, range);
|
||||
allocator.push(expr);
|
||||
stacks.push((in_annotation, context));
|
||||
stacks.push(((in_annotation, in_type_checking_block), deferral));
|
||||
} else {
|
||||
if self
|
||||
.settings
|
||||
@@ -4444,10 +4552,13 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (expr, (in_annotation, (scopes, parents))) in allocator.iter().zip(stacks) {
|
||||
for (expr, ((in_annotation, in_type_checking_block), (scopes, parents))) in
|
||||
allocator.iter().zip(stacks)
|
||||
{
|
||||
self.scope_stack = scopes;
|
||||
self.parents = parents;
|
||||
self.in_annotation = in_annotation;
|
||||
self.in_type_checking_block = in_type_checking_block;
|
||||
self.in_type_definition = true;
|
||||
self.in_deferred_string_type_definition = true;
|
||||
self.visit_expr(expr);
|
||||
@@ -4578,11 +4689,54 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark anything referenced in `__all__` as used.
|
||||
let global_scope = &self.scopes[GLOBAL_SCOPE_INDEX];
|
||||
let all_names: Option<(&Vec<String>, Range)> = global_scope
|
||||
.bindings
|
||||
.get("__all__")
|
||||
.map(|index| &self.bindings[*index])
|
||||
.and_then(|binding| match &binding.kind {
|
||||
BindingKind::Export(names) => Some((names, binding.range)),
|
||||
_ => None,
|
||||
});
|
||||
let all_bindings: Option<(Vec<usize>, Range)> = all_names.map(|(names, range)| {
|
||||
(
|
||||
names
|
||||
.iter()
|
||||
.filter_map(|name| global_scope.bindings.get(name.as_str()).copied())
|
||||
.collect(),
|
||||
range,
|
||||
)
|
||||
});
|
||||
if let Some((bindings, range)) = all_bindings {
|
||||
for index in bindings {
|
||||
self.bindings[index].mark_used(
|
||||
GLOBAL_SCOPE_INDEX,
|
||||
range,
|
||||
ExecutionContext::Runtime,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract `__all__` names from the global scope.
|
||||
let all_names: Option<(Vec<&str>, Range)> = global_scope
|
||||
.bindings
|
||||
.get("__all__")
|
||||
.map(|index| &self.bindings[*index])
|
||||
.and_then(|binding| match &binding.kind {
|
||||
BindingKind::Export(names) => {
|
||||
Some((names.iter().map(String::as_str).collect(), binding.range))
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
// Identify any valid runtime imports. If a module is imported at runtime, and
|
||||
// used at runtime, then by default, we avoid flagging any other
|
||||
// imports from that model as typing-only.
|
||||
let runtime_imports: Vec<Vec<&Binding>> = if !self.settings.flake8_type_checking.strict
|
||||
&& (self
|
||||
let runtime_imports: Vec<Vec<&Binding>> = if self.settings.flake8_type_checking.strict {
|
||||
vec![]
|
||||
} else {
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::RuntimeImportInTypeCheckingBlock)
|
||||
@@ -4597,29 +4751,41 @@ impl<'a> Checker<'a> {
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::TypingOnlyStandardLibraryImport))
|
||||
{
|
||||
self.scopes
|
||||
.iter()
|
||||
.map(|scope| {
|
||||
scope
|
||||
.bindings
|
||||
.values()
|
||||
.map(|index| &self.bindings[*index])
|
||||
.filter(|binding| {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(binding)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
vec![]
|
||||
.enabled(&Rule::TypingOnlyStandardLibraryImport)
|
||||
{
|
||||
self.scopes
|
||||
.iter()
|
||||
.map(|scope| {
|
||||
scope
|
||||
.bindings
|
||||
.values()
|
||||
.map(|index| &self.bindings[*index])
|
||||
.filter(|binding| {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(binding)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
for (index, stack) in self.dead_scopes.iter().rev() {
|
||||
let scope = &self.scopes[*index];
|
||||
|
||||
// F822
|
||||
if *index == GLOBAL_SCOPE_INDEX {
|
||||
if self.settings.rules.enabled(&Rule::UndefinedExport) {
|
||||
if let Some((names, range)) = &all_names {
|
||||
diagnostics.extend(pyflakes::rules::undefined_export(
|
||||
names, range, self.path, scope,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PLW0602
|
||||
if self
|
||||
.settings
|
||||
@@ -4648,35 +4814,6 @@ impl<'a> Checker<'a> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let all_binding: Option<&Binding> = scope
|
||||
.bindings
|
||||
.get("__all__")
|
||||
.map(|index| &self.bindings[*index]);
|
||||
let all_names: Option<Vec<&str>> =
|
||||
all_binding.and_then(|binding| match &binding.kind {
|
||||
BindingKind::Export(names) => Some(names.iter().map(String::as_str).collect()),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if self.settings.rules.enabled(&Rule::UndefinedExport) {
|
||||
if !scope.import_starred && !self.path.ends_with("__init__.py") {
|
||||
if let Some(all_binding) = all_binding {
|
||||
if let Some(names) = &all_names {
|
||||
for &name in names {
|
||||
if !scope.bindings.contains_key(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::UndefinedExport {
|
||||
name: name.to_string(),
|
||||
},
|
||||
all_binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for any bindings that were redefined in another scope, and remain
|
||||
// unused. Note that we only store references in `redefinitions` if
|
||||
// the bindings are in different scopes.
|
||||
@@ -4692,13 +4829,7 @@ impl<'a> Checker<'a> {
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::FutureImportation
|
||||
) {
|
||||
// Skip used exports from `__all__`
|
||||
if binding.used()
|
||||
|| all_names
|
||||
.as_ref()
|
||||
.map(|names| names.contains(name))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if binding.used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4728,31 +4859,27 @@ impl<'a> Checker<'a> {
|
||||
|
||||
if self.settings.rules.enabled(&Rule::ImportStarUsage) {
|
||||
if scope.import_starred {
|
||||
if let Some(all_binding) = all_binding {
|
||||
if let Some(names) = &all_names {
|
||||
let mut from_list = vec![];
|
||||
for binding in
|
||||
scope.bindings.values().map(|index| &self.bindings[*index])
|
||||
{
|
||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||
from_list.push(helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
));
|
||||
}
|
||||
if let Some((names, range)) = &all_names {
|
||||
let mut from_list = vec![];
|
||||
for binding in scope.bindings.values().map(|index| &self.bindings[*index]) {
|
||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||
from_list.push(helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
));
|
||||
}
|
||||
from_list.sort();
|
||||
}
|
||||
from_list.sort();
|
||||
|
||||
for &name in names {
|
||||
if !scope.bindings.contains_key(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: name.to_string(),
|
||||
sources: from_list.clone(),
|
||||
},
|
||||
all_binding.range,
|
||||
));
|
||||
}
|
||||
for &name in names {
|
||||
if !scope.bindings.contains_key(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: name.to_string(),
|
||||
sources: from_list.clone(),
|
||||
},
|
||||
*range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4820,7 +4947,7 @@ impl<'a> Checker<'a> {
|
||||
let mut ignored: FxHashMap<BindingContext, Vec<UnusedImport>> =
|
||||
FxHashMap::default();
|
||||
|
||||
for (name, index) in &scope.bindings {
|
||||
for index in scope.bindings.values() {
|
||||
let binding = &self.bindings[*index];
|
||||
|
||||
let full_name = match &binding.kind {
|
||||
@@ -4830,13 +4957,7 @@ impl<'a> Checker<'a> {
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// Skip used exports from `__all__`
|
||||
if binding.used()
|
||||
|| all_names
|
||||
.as_ref()
|
||||
.map(|names| names.contains(name))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if binding.used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4853,9 +4974,9 @@ impl<'a> Checker<'a> {
|
||||
None
|
||||
};
|
||||
|
||||
if self.is_ignored(&Rule::UnusedImport, diagnostic_lineno)
|
||||
if self.rule_is_ignored(&Rule::UnusedImport, diagnostic_lineno)
|
||||
|| parent_lineno.map_or(false, |parent_lineno| {
|
||||
self.is_ignored(&Rule::UnusedImport, parent_lineno)
|
||||
self.rule_is_ignored(&Rule::UnusedImport, parent_lineno)
|
||||
})
|
||||
{
|
||||
ignored
|
||||
@@ -4987,10 +5108,7 @@ impl<'a> Checker<'a> {
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypeClassMethod)
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression);
|
||||
|| self.settings.rules.enabled(&Rule::AnyType);
|
||||
let enforce_docstrings = self.settings.rules.enabled(&Rule::PublicModule)
|
||||
|| self.settings.rules.enabled(&Rule::PublicClass)
|
||||
|| self.settings.rules.enabled(&Rule::PublicMethod)
|
||||
@@ -5030,16 +5148,16 @@ impl<'a> Checker<'a> {
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::SectionUnderlineNotOverIndented)
|
||||
|| self.settings.rules.enabled(&Rule::UsesTripleQuotes)
|
||||
|| self.settings.rules.enabled(&Rule::TripleSingleQuotes)
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UsesRPrefixForBackslashedContent)
|
||||
.enabled(&Rule::EscapeSequenceInDocstring)
|
||||
|| self.settings.rules.enabled(&Rule::EndsInPeriod)
|
||||
|| self.settings.rules.enabled(&Rule::NonImperativeMood)
|
||||
|| self.settings.rules.enabled(&Rule::NoSignature)
|
||||
|| self.settings.rules.enabled(&Rule::FirstLineCapitalized)
|
||||
|| self.settings.rules.enabled(&Rule::NoThisPrefix)
|
||||
|| self.settings.rules.enabled(&Rule::DocstringStartsWithThis)
|
||||
|| self.settings.rules.enabled(&Rule::CapitalizeSectionName)
|
||||
|| self.settings.rules.enabled(&Rule::NewLineAfterSectionName)
|
||||
|| self
|
||||
@@ -5064,12 +5182,12 @@ impl<'a> Checker<'a> {
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::BlankLineAfterLastSection)
|
||||
|| self.settings.rules.enabled(&Rule::NonEmptySection)
|
||||
|| self.settings.rules.enabled(&Rule::EmptyDocstringSection)
|
||||
|| self.settings.rules.enabled(&Rule::EndsInPunctuation)
|
||||
|| self.settings.rules.enabled(&Rule::SectionNameEndsInColon)
|
||||
|| self.settings.rules.enabled(&Rule::DocumentAllArguments)
|
||||
|| self.settings.rules.enabled(&Rule::SkipDocstring)
|
||||
|| self.settings.rules.enabled(&Rule::NonEmpty);
|
||||
|| self.settings.rules.enabled(&Rule::UndocumentedParam)
|
||||
|| self.settings.rules.enabled(&Rule::OverloadWithDocstring)
|
||||
|| self.settings.rules.enabled(&Rule::EmptyDocstring);
|
||||
|
||||
let mut overloaded_name: Option<String> = None;
|
||||
self.definitions.reverse();
|
||||
@@ -5110,10 +5228,8 @@ impl<'a> Checker<'a> {
|
||||
|
||||
// Extract a `Docstring` from a `Definition`.
|
||||
let expr = definition.docstring.unwrap();
|
||||
let contents = self
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(expr));
|
||||
let indentation = self.locator.slice_source_code_range(&Range::new(
|
||||
let contents = self.locator.slice(&Range::from_located(expr));
|
||||
let indentation = self.locator.slice(&Range::new(
|
||||
Location::new(expr.location.row(), 0),
|
||||
Location::new(expr.location.row(), expr.location.column()),
|
||||
));
|
||||
@@ -5178,13 +5294,13 @@ impl<'a> Checker<'a> {
|
||||
{
|
||||
pydocstyle::rules::multi_line_summary_start(self, &docstring);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UsesTripleQuotes) {
|
||||
if self.settings.rules.enabled(&Rule::TripleSingleQuotes) {
|
||||
pydocstyle::rules::triple_quotes(self, &docstring);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UsesRPrefixForBackslashedContent)
|
||||
.enabled(&Rule::EscapeSequenceInDocstring)
|
||||
{
|
||||
pydocstyle::rules::backslashes(self, &docstring);
|
||||
}
|
||||
@@ -5200,13 +5316,13 @@ impl<'a> Checker<'a> {
|
||||
if self.settings.rules.enabled(&Rule::FirstLineCapitalized) {
|
||||
pydocstyle::rules::capitalized(self, &docstring);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NoThisPrefix) {
|
||||
if self.settings.rules.enabled(&Rule::DocstringStartsWithThis) {
|
||||
pydocstyle::rules::starts_with_this(self, &docstring);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::EndsInPunctuation) {
|
||||
pydocstyle::rules::ends_with_punctuation(self, &docstring);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::SkipDocstring) {
|
||||
if self.settings.rules.enabled(&Rule::OverloadWithDocstring) {
|
||||
pydocstyle::rules::if_needed(self, &docstring);
|
||||
}
|
||||
if self
|
||||
@@ -5242,9 +5358,9 @@ impl<'a> Checker<'a> {
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::BlankLineAfterLastSection)
|
||||
|| self.settings.rules.enabled(&Rule::NonEmptySection)
|
||||
|| self.settings.rules.enabled(&Rule::EmptyDocstringSection)
|
||||
|| self.settings.rules.enabled(&Rule::SectionNameEndsInColon)
|
||||
|| self.settings.rules.enabled(&Rule::DocumentAllArguments)
|
||||
|| self.settings.rules.enabled(&Rule::UndocumentedParam)
|
||||
{
|
||||
pydocstyle::rules::sections(
|
||||
self,
|
||||
|
||||
@@ -52,8 +52,7 @@ pub fn check_logical_lines(
|
||||
|
||||
// Extract the indentation level.
|
||||
let start_loc = line.mapping[0].1;
|
||||
let start_line = locator
|
||||
.slice_source_code_range(&Range::new(Location::new(start_loc.row(), 0), start_loc));
|
||||
let start_line = locator.slice(&Range::new(Location::new(start_loc.row(), 0), start_loc));
|
||||
let indent_level = expand_indent(start_line);
|
||||
let indent_size = 4;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use nohash_hasher::IntMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::codes::NoqaCode;
|
||||
use crate::fix::Fix;
|
||||
use crate::noqa;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
@@ -20,7 +21,7 @@ pub fn check_noqa(
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) {
|
||||
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
|
||||
let mut noqa_directives: IntMap<usize, (Directive, Vec<NoqaCode>)> = IntMap::default();
|
||||
let mut ignored = vec![];
|
||||
|
||||
let enforce_noqa = settings.rules.enabled(&Rule::UnusedNOQA);
|
||||
@@ -55,13 +56,13 @@ pub fn check_noqa(
|
||||
});
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored.push(index);
|
||||
continue;
|
||||
}
|
||||
@@ -82,12 +83,12 @@ pub fn check_noqa(
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno - 1]), vec![]));
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored.push(index);
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored.push(index);
|
||||
}
|
||||
}
|
||||
@@ -128,12 +129,12 @@ pub fn check_noqa(
|
||||
let mut self_ignore = false;
|
||||
for code in codes {
|
||||
let code = get_redirect_target(code).unwrap_or(code);
|
||||
if code == Rule::UnusedNOQA.code() {
|
||||
if Rule::UnusedNOQA.noqa_code() == code {
|
||||
self_ignore = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
if matches.iter().any(|m| *m == code) || settings.external.contains(code) {
|
||||
valid_codes.push(code);
|
||||
} else {
|
||||
if let Ok(rule) = Rule::from_code(code) {
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::rules::pycodestyle::rules::{
|
||||
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
|
||||
};
|
||||
use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
|
||||
use crate::rules::pylint;
|
||||
use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Stylist;
|
||||
@@ -37,15 +38,12 @@ pub fn check_physical_lines(
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(&Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = settings.rules.enabled(&Rule::LineTooLong);
|
||||
let enforce_no_newline_at_end_of_file = settings.rules.enabled(&Rule::NoNewLineAtEndOfFile);
|
||||
let enforce_unnecessary_coding_comment = settings
|
||||
.rules
|
||||
.enabled(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
let enforce_unnecessary_coding_comment = settings.rules.enabled(&Rule::UTF8EncodingDeclaration);
|
||||
let enforce_mixed_spaces_and_tabs = settings.rules.enabled(&Rule::MixedSpacesAndTabs);
|
||||
let enforce_bidirectional_unicode = settings.rules.enabled(&Rule::BidirectionalUnicode);
|
||||
|
||||
let fix_unnecessary_coding_comment = matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings
|
||||
.rules
|
||||
.should_fix(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
&& settings.rules.should_fix(&Rule::UTF8EncodingDeclaration);
|
||||
let fix_shebang_whitespace = matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::ShebangWhitespace);
|
||||
|
||||
@@ -137,6 +135,10 @@ pub fn check_physical_lines(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_bidirectional_unicode {
|
||||
diagnostics.extend(pylint::rules::bidirectional_unicode(index, line));
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_no_newline_at_end_of_file {
|
||||
|
||||
@@ -32,8 +32,15 @@ pub fn check_tokens(
|
||||
let enforce_quotes = settings.rules.enabled(&Rule::BadQuotesInlineString)
|
||||
|| settings.rules.enabled(&Rule::BadQuotesMultilineString)
|
||||
|| settings.rules.enabled(&Rule::BadQuotesDocstring)
|
||||
|| settings.rules.enabled(&Rule::AvoidQuoteEscape);
|
||||
|| settings.rules.enabled(&Rule::AvoidableEscapedQuote);
|
||||
let enforce_commented_out_code = settings.rules.enabled(&Rule::CommentedOutCode);
|
||||
let enforce_compound_statements = settings
|
||||
.rules
|
||||
.enabled(&Rule::MultipleStatementsOnOneLineColon)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::MultipleStatementsOnOneLineSemicolon)
|
||||
|| settings.rules.enabled(&Rule::UselessSemicolon);
|
||||
let enforce_invalid_escape_sequence = settings.rules.enabled(&Rule::InvalidEscapeSequence);
|
||||
let enforce_implicit_string_concatenation = settings
|
||||
.rules
|
||||
@@ -48,10 +55,8 @@ pub fn check_tokens(
|
||||
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
|
||||
let enforce_extraneous_parenthesis = settings.rules.enabled(&Rule::ExtraneousParentheses);
|
||||
|
||||
if enforce_ambiguous_unicode_character
|
||||
|| enforce_commented_out_code
|
||||
|| enforce_invalid_escape_sequence
|
||||
{
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
let is_docstring = if enforce_ambiguous_unicode_character {
|
||||
@@ -60,54 +65,64 @@ pub fn check_tokens(
|
||||
false
|
||||
};
|
||||
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment(_)) {
|
||||
diagnostics.extend(ruff::rules::ambiguous_unicode_character(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if is_docstring {
|
||||
Context::Docstring
|
||||
} else {
|
||||
Context::String
|
||||
}
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment(_)) {
|
||||
diagnostics.extend(ruff::rules::ambiguous_unicode_character(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if is_docstring {
|
||||
Context::Docstring
|
||||
} else {
|
||||
Context::Comment
|
||||
},
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
}
|
||||
Context::String
|
||||
}
|
||||
} else {
|
||||
Context::Comment
|
||||
},
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eradicate
|
||||
if enforce_commented_out_code {
|
||||
if matches!(tok, Tok::Comment(_)) {
|
||||
if let Some(diagnostic) =
|
||||
eradicate::rules::commented_out_code(locator, start, end, settings, autofix)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(pycodestyle::rules::invalid_escape_sequence(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::InvalidEscapeSequence),
|
||||
));
|
||||
// ERA001
|
||||
if enforce_commented_out_code {
|
||||
for (start, tok, end) in tokens.iter().flatten() {
|
||||
if matches!(tok, Tok::Comment(_)) {
|
||||
if let Some(diagnostic) =
|
||||
eradicate::rules::commented_out_code(locator, *start, *end, settings, autofix)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
for (start, tok, end) in tokens.iter().flatten() {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(pycodestyle::rules::invalid_escape_sequence(
|
||||
locator,
|
||||
*start,
|
||||
*end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::InvalidEscapeSequence),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// E701, E702, E703
|
||||
if enforce_compound_statements {
|
||||
diagnostics.extend(
|
||||
pycodestyle::rules::compound_statements(tokens)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// Q001, Q002, Q003
|
||||
if enforce_quotes {
|
||||
diagnostics.extend(
|
||||
|
||||
609
crates/ruff/src/codes.rs
Normal file
609
crates/ruff/src/codes.rs
Normal file
@@ -0,0 +1,609 @@
|
||||
use crate::registry::{Linter, Rule};
|
||||
|
||||
#[ruff_macros::map_codes]
|
||||
pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Linter::*;
|
||||
|
||||
Some(match (linter, code) {
|
||||
// pycodestyle errors
|
||||
(Pycodestyle, "E101") => Rule::MixedSpacesAndTabs,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E111") => Rule::IndentationWithInvalidMultiple,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E112") => Rule::NoIndentedBlock,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E113") => Rule::UnexpectedIndentation,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E114") => Rule::IndentationWithInvalidMultipleComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E115") => Rule::NoIndentedBlockComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E116") => Rule::UnexpectedIndentationComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E117") => Rule::OverIndented,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E201") => Rule::WhitespaceAfterOpenBracket,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E202") => Rule::WhitespaceBeforeCloseBracket,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E203") => Rule::WhitespaceBeforePunctuation,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E221") => Rule::MultipleSpacesBeforeOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E222") => Rule::MultipleSpacesAfterOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E223") => Rule::TabBeforeOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E224") => Rule::TabAfterOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E261") => Rule::TooFewSpacesBeforeInlineComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E262") => Rule::NoSpaceAfterInlineComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E265") => Rule::NoSpaceAfterBlockComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E266") => Rule::MultipleLeadingHashesForBlockComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E271") => Rule::MultipleSpacesAfterKeyword,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E272") => Rule::MultipleSpacesBeforeKeyword,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E273") => Rule::TabAfterKeyword,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E274") => Rule::TabBeforeKeyword,
|
||||
(Pycodestyle, "E401") => Rule::MultipleImportsOnOneLine,
|
||||
(Pycodestyle, "E402") => Rule::ModuleImportNotAtTopOfFile,
|
||||
(Pycodestyle, "E501") => Rule::LineTooLong,
|
||||
(Pycodestyle, "E701") => Rule::MultipleStatementsOnOneLineColon,
|
||||
(Pycodestyle, "E702") => Rule::MultipleStatementsOnOneLineSemicolon,
|
||||
(Pycodestyle, "E703") => Rule::UselessSemicolon,
|
||||
(Pycodestyle, "E711") => Rule::NoneComparison,
|
||||
(Pycodestyle, "E712") => Rule::TrueFalseComparison,
|
||||
(Pycodestyle, "E713") => Rule::NotInTest,
|
||||
(Pycodestyle, "E714") => Rule::NotIsTest,
|
||||
(Pycodestyle, "E721") => Rule::TypeComparison,
|
||||
(Pycodestyle, "E722") => Rule::BareExcept,
|
||||
(Pycodestyle, "E731") => Rule::LambdaAssignment,
|
||||
(Pycodestyle, "E741") => Rule::AmbiguousVariableName,
|
||||
(Pycodestyle, "E742") => Rule::AmbiguousClassName,
|
||||
(Pycodestyle, "E743") => Rule::AmbiguousFunctionName,
|
||||
(Pycodestyle, "E902") => Rule::IOError,
|
||||
(Pycodestyle, "E999") => Rule::SyntaxError,
|
||||
|
||||
// pycodestyle warnings
|
||||
(Pycodestyle, "W292") => Rule::NoNewLineAtEndOfFile,
|
||||
(Pycodestyle, "W505") => Rule::DocLineTooLong,
|
||||
(Pycodestyle, "W605") => Rule::InvalidEscapeSequence,
|
||||
|
||||
// pyflakes
|
||||
(Pyflakes, "401") => Rule::UnusedImport,
|
||||
(Pyflakes, "402") => Rule::ImportShadowedByLoopVar,
|
||||
(Pyflakes, "403") => Rule::ImportStar,
|
||||
(Pyflakes, "404") => Rule::LateFutureImport,
|
||||
(Pyflakes, "405") => Rule::ImportStarUsage,
|
||||
(Pyflakes, "406") => Rule::ImportStarNotPermitted,
|
||||
(Pyflakes, "407") => Rule::FutureFeatureNotDefined,
|
||||
(Pyflakes, "501") => Rule::PercentFormatInvalidFormat,
|
||||
(Pyflakes, "502") => Rule::PercentFormatExpectedMapping,
|
||||
(Pyflakes, "503") => Rule::PercentFormatExpectedSequence,
|
||||
(Pyflakes, "504") => Rule::PercentFormatExtraNamedArguments,
|
||||
(Pyflakes, "505") => Rule::PercentFormatMissingArgument,
|
||||
(Pyflakes, "506") => Rule::PercentFormatMixedPositionalAndNamed,
|
||||
(Pyflakes, "507") => Rule::PercentFormatPositionalCountMismatch,
|
||||
(Pyflakes, "508") => Rule::PercentFormatStarRequiresSequence,
|
||||
(Pyflakes, "509") => Rule::PercentFormatUnsupportedFormatCharacter,
|
||||
(Pyflakes, "521") => Rule::StringDotFormatInvalidFormat,
|
||||
(Pyflakes, "522") => Rule::StringDotFormatExtraNamedArguments,
|
||||
(Pyflakes, "523") => Rule::StringDotFormatExtraPositionalArguments,
|
||||
(Pyflakes, "524") => Rule::StringDotFormatMissingArguments,
|
||||
(Pyflakes, "525") => Rule::StringDotFormatMixingAutomatic,
|
||||
(Pyflakes, "541") => Rule::FStringMissingPlaceholders,
|
||||
(Pyflakes, "601") => Rule::MultiValueRepeatedKeyLiteral,
|
||||
(Pyflakes, "602") => Rule::MultiValueRepeatedKeyVariable,
|
||||
(Pyflakes, "621") => Rule::ExpressionsInStarAssignment,
|
||||
(Pyflakes, "622") => Rule::TwoStarredExpressions,
|
||||
(Pyflakes, "631") => Rule::AssertTuple,
|
||||
(Pyflakes, "632") => Rule::IsLiteral,
|
||||
(Pyflakes, "633") => Rule::InvalidPrintSyntax,
|
||||
(Pyflakes, "634") => Rule::IfTuple,
|
||||
(Pyflakes, "701") => Rule::BreakOutsideLoop,
|
||||
(Pyflakes, "702") => Rule::ContinueOutsideLoop,
|
||||
(Pyflakes, "704") => Rule::YieldOutsideFunction,
|
||||
(Pyflakes, "706") => Rule::ReturnOutsideFunction,
|
||||
(Pyflakes, "707") => Rule::DefaultExceptNotLast,
|
||||
(Pyflakes, "722") => Rule::ForwardAnnotationSyntaxError,
|
||||
(Pyflakes, "811") => Rule::RedefinedWhileUnused,
|
||||
(Pyflakes, "821") => Rule::UndefinedName,
|
||||
(Pyflakes, "822") => Rule::UndefinedExport,
|
||||
(Pyflakes, "823") => Rule::UndefinedLocal,
|
||||
(Pyflakes, "841") => Rule::UnusedVariable,
|
||||
(Pyflakes, "842") => Rule::UnusedAnnotation,
|
||||
(Pyflakes, "901") => Rule::RaiseNotImplemented,
|
||||
|
||||
// pylint
|
||||
(Pylint, "E0100") => Rule::YieldInInit,
|
||||
(Pylint, "E0604") => Rule::InvalidAllObject,
|
||||
(Pylint, "E0605") => Rule::InvalidAllFormat,
|
||||
(Pylint, "E1307") => Rule::BadStringFormatType,
|
||||
(Pylint, "E2502") => Rule::BidirectionalUnicode,
|
||||
(Pylint, "E1310") => Rule::BadStrStripCall,
|
||||
(Pylint, "C0414") => Rule::UselessImportAlias,
|
||||
(Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
|
||||
(Pylint, "E0117") => Rule::NonlocalWithoutBinding,
|
||||
(Pylint, "E0118") => Rule::UsedPriorGlobalDeclaration,
|
||||
(Pylint, "E1142") => Rule::AwaitOutsideAsync,
|
||||
(Pylint, "R0206") => Rule::PropertyWithParameters,
|
||||
(Pylint, "R0402") => Rule::ConsiderUsingFromImport,
|
||||
(Pylint, "R0133") => Rule::ComparisonOfConstant,
|
||||
(Pylint, "R1701") => Rule::ConsiderMergingIsinstance,
|
||||
(Pylint, "R1722") => Rule::ConsiderUsingSysExit,
|
||||
(Pylint, "R2004") => Rule::MagicValueComparison,
|
||||
(Pylint, "W0120") => Rule::UselessElseOnLoop,
|
||||
(Pylint, "W0602") => Rule::GlobalVariableNotAssigned,
|
||||
(Pylint, "R0911") => Rule::TooManyReturnStatements,
|
||||
(Pylint, "R0913") => Rule::TooManyArguments,
|
||||
(Pylint, "R0912") => Rule::TooManyBranches,
|
||||
(Pylint, "R0915") => Rule::TooManyStatements,
|
||||
|
||||
// flake8-builtins
|
||||
(Flake8Builtins, "001") => Rule::BuiltinVariableShadowing,
|
||||
(Flake8Builtins, "002") => Rule::BuiltinArgumentShadowing,
|
||||
(Flake8Builtins, "003") => Rule::BuiltinAttributeShadowing,
|
||||
|
||||
// flake8-bugbear
|
||||
(Flake8Bugbear, "002") => Rule::UnaryPrefixIncrement,
|
||||
(Flake8Bugbear, "003") => Rule::AssignmentToOsEnviron,
|
||||
(Flake8Bugbear, "004") => Rule::UnreliableCallableCheck,
|
||||
(Flake8Bugbear, "005") => Rule::StripWithMultiCharacters,
|
||||
(Flake8Bugbear, "006") => Rule::MutableArgumentDefault,
|
||||
(Flake8Bugbear, "007") => Rule::UnusedLoopControlVariable,
|
||||
(Flake8Bugbear, "008") => Rule::FunctionCallArgumentDefault,
|
||||
(Flake8Bugbear, "009") => Rule::GetAttrWithConstant,
|
||||
(Flake8Bugbear, "010") => Rule::SetAttrWithConstant,
|
||||
(Flake8Bugbear, "011") => Rule::AssertFalse,
|
||||
(Flake8Bugbear, "012") => Rule::JumpStatementInFinally,
|
||||
(Flake8Bugbear, "013") => Rule::RedundantTupleInExceptionHandler,
|
||||
(Flake8Bugbear, "014") => Rule::DuplicateHandlerException,
|
||||
(Flake8Bugbear, "015") => Rule::UselessComparison,
|
||||
(Flake8Bugbear, "016") => Rule::CannotRaiseLiteral,
|
||||
(Flake8Bugbear, "017") => Rule::AssertRaisesException,
|
||||
(Flake8Bugbear, "018") => Rule::UselessExpression,
|
||||
(Flake8Bugbear, "019") => Rule::CachedInstanceMethod,
|
||||
(Flake8Bugbear, "020") => Rule::LoopVariableOverridesIterator,
|
||||
(Flake8Bugbear, "021") => Rule::FStringDocstring,
|
||||
(Flake8Bugbear, "022") => Rule::UselessContextlibSuppress,
|
||||
(Flake8Bugbear, "023") => Rule::FunctionUsesLoopVariable,
|
||||
(Flake8Bugbear, "024") => Rule::AbstractBaseClassWithoutAbstractMethod,
|
||||
(Flake8Bugbear, "025") => Rule::DuplicateTryBlockException,
|
||||
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
|
||||
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
|
||||
(Flake8Bugbear, "904") => Rule::RaiseWithoutFromInsideExcept,
|
||||
(Flake8Bugbear, "905") => Rule::ZipWithoutExplicitStrict,
|
||||
|
||||
// flake8-blind-except
|
||||
(Flake8BlindExcept, "001") => Rule::BlindExcept,
|
||||
|
||||
// flake8-comprehensions
|
||||
(Flake8Comprehensions, "00") => Rule::UnnecessaryGeneratorList,
|
||||
(Flake8Comprehensions, "01") => Rule::UnnecessaryGeneratorSet,
|
||||
(Flake8Comprehensions, "02") => Rule::UnnecessaryGeneratorDict,
|
||||
(Flake8Comprehensions, "03") => Rule::UnnecessaryListComprehensionSet,
|
||||
(Flake8Comprehensions, "04") => Rule::UnnecessaryListComprehensionDict,
|
||||
(Flake8Comprehensions, "05") => Rule::UnnecessaryLiteralSet,
|
||||
(Flake8Comprehensions, "06") => Rule::UnnecessaryLiteralDict,
|
||||
(Flake8Comprehensions, "08") => Rule::UnnecessaryCollectionCall,
|
||||
(Flake8Comprehensions, "09") => Rule::UnnecessaryLiteralWithinTupleCall,
|
||||
(Flake8Comprehensions, "10") => Rule::UnnecessaryLiteralWithinListCall,
|
||||
(Flake8Comprehensions, "11") => Rule::UnnecessaryListCall,
|
||||
(Flake8Comprehensions, "13") => Rule::UnnecessaryCallAroundSorted,
|
||||
(Flake8Comprehensions, "14") => Rule::UnnecessaryDoubleCastOrProcess,
|
||||
(Flake8Comprehensions, "15") => Rule::UnnecessarySubscriptReversal,
|
||||
(Flake8Comprehensions, "16") => Rule::UnnecessaryComprehension,
|
||||
(Flake8Comprehensions, "17") => Rule::UnnecessaryMap,
|
||||
|
||||
// flake8-debugger
|
||||
(Flake8Debugger, "0") => Rule::Debugger,
|
||||
|
||||
// mccabe
|
||||
(McCabe, "1") => Rule::ComplexStructure,
|
||||
|
||||
// flake8-tidy-imports
|
||||
(Flake8TidyImports, "251") => Rule::BannedApi,
|
||||
(Flake8TidyImports, "252") => Rule::RelativeImports,
|
||||
|
||||
// flake8-return
|
||||
(Flake8Return, "501") => Rule::UnnecessaryReturnNone,
|
||||
(Flake8Return, "502") => Rule::ImplicitReturnValue,
|
||||
(Flake8Return, "503") => Rule::ImplicitReturn,
|
||||
(Flake8Return, "504") => Rule::UnnecessaryAssign,
|
||||
(Flake8Return, "505") => Rule::SuperfluousElseReturn,
|
||||
(Flake8Return, "506") => Rule::SuperfluousElseRaise,
|
||||
(Flake8Return, "507") => Rule::SuperfluousElseContinue,
|
||||
(Flake8Return, "508") => Rule::SuperfluousElseBreak,
|
||||
|
||||
// flake8-implicit-str-concat
|
||||
(Flake8ImplicitStrConcat, "001") => Rule::SingleLineImplicitStringConcatenation,
|
||||
(Flake8ImplicitStrConcat, "002") => Rule::MultiLineImplicitStringConcatenation,
|
||||
(Flake8ImplicitStrConcat, "003") => Rule::ExplicitStringConcatenation,
|
||||
|
||||
// flake8-print
|
||||
(Flake8Print, "1") => Rule::PrintFound,
|
||||
(Flake8Print, "3") => Rule::PPrintFound,
|
||||
|
||||
// flake8-quotes
|
||||
(Flake8Quotes, "000") => Rule::BadQuotesInlineString,
|
||||
(Flake8Quotes, "001") => Rule::BadQuotesMultilineString,
|
||||
(Flake8Quotes, "002") => Rule::BadQuotesDocstring,
|
||||
(Flake8Quotes, "003") => Rule::AvoidableEscapedQuote,
|
||||
|
||||
// flake8-annotations
|
||||
(Flake8Annotations, "001") => Rule::MissingTypeFunctionArgument,
|
||||
(Flake8Annotations, "002") => Rule::MissingTypeArgs,
|
||||
(Flake8Annotations, "003") => Rule::MissingTypeKwargs,
|
||||
(Flake8Annotations, "101") => Rule::MissingTypeSelf,
|
||||
(Flake8Annotations, "102") => Rule::MissingTypeCls,
|
||||
(Flake8Annotations, "201") => Rule::MissingReturnTypePublicFunction,
|
||||
(Flake8Annotations, "202") => Rule::MissingReturnTypePrivateFunction,
|
||||
(Flake8Annotations, "204") => Rule::MissingReturnTypeSpecialMethod,
|
||||
(Flake8Annotations, "205") => Rule::MissingReturnTypeStaticMethod,
|
||||
(Flake8Annotations, "206") => Rule::MissingReturnTypeClassMethod,
|
||||
(Flake8Annotations, "401") => Rule::AnyType,
|
||||
|
||||
// flake8-2020
|
||||
(Flake82020, "101") => Rule::SysVersionSlice3Referenced,
|
||||
(Flake82020, "102") => Rule::SysVersion2Referenced,
|
||||
(Flake82020, "103") => Rule::SysVersionCmpStr3,
|
||||
(Flake82020, "201") => Rule::SysVersionInfo0Eq3Referenced,
|
||||
(Flake82020, "202") => Rule::SixPY3Referenced,
|
||||
(Flake82020, "203") => Rule::SysVersionInfo1CmpInt,
|
||||
(Flake82020, "204") => Rule::SysVersionInfoMinorCmpInt,
|
||||
(Flake82020, "301") => Rule::SysVersion0Referenced,
|
||||
(Flake82020, "302") => Rule::SysVersionCmpStr10,
|
||||
(Flake82020, "303") => Rule::SysVersionSlice1Referenced,
|
||||
|
||||
// flake8-simplify
|
||||
(Flake8Simplify, "101") => Rule::DuplicateIsinstanceCall,
|
||||
(Flake8Simplify, "102") => Rule::CollapsibleIf,
|
||||
(Flake8Simplify, "103") => Rule::NeedlessBool,
|
||||
(Flake8Simplify, "105") => Rule::UseContextlibSuppress,
|
||||
(Flake8Simplify, "107") => Rule::ReturnInTryExceptFinally,
|
||||
(Flake8Simplify, "108") => Rule::UseTernaryOperator,
|
||||
(Flake8Simplify, "109") => Rule::CompareWithTuple,
|
||||
(Flake8Simplify, "110") => Rule::ConvertLoopToAny,
|
||||
(Flake8Simplify, "111") => Rule::ConvertLoopToAll,
|
||||
(Flake8Simplify, "112") => Rule::UseCapitalEnvironmentVariables,
|
||||
(Flake8Simplify, "114") => Rule::IfWithSameArms,
|
||||
(Flake8Simplify, "115") => Rule::OpenFileWithContextHandler,
|
||||
(Flake8Simplify, "117") => Rule::MultipleWithStatements,
|
||||
(Flake8Simplify, "118") => Rule::KeyInDict,
|
||||
(Flake8Simplify, "201") => Rule::NegateEqualOp,
|
||||
(Flake8Simplify, "202") => Rule::NegateNotEqualOp,
|
||||
(Flake8Simplify, "208") => Rule::DoubleNegation,
|
||||
(Flake8Simplify, "210") => Rule::IfExprWithTrueFalse,
|
||||
(Flake8Simplify, "211") => Rule::IfExprWithFalseTrue,
|
||||
(Flake8Simplify, "212") => Rule::IfExprWithTwistedArms,
|
||||
(Flake8Simplify, "220") => Rule::AAndNotA,
|
||||
(Flake8Simplify, "221") => Rule::AOrNotA,
|
||||
(Flake8Simplify, "222") => Rule::OrTrue,
|
||||
(Flake8Simplify, "223") => Rule::AndFalse,
|
||||
(Flake8Simplify, "300") => Rule::YodaConditions,
|
||||
(Flake8Simplify, "401") => Rule::DictGetWithDefault,
|
||||
|
||||
// pyupgrade
|
||||
(Pyupgrade, "001") => Rule::UselessMetaclassType,
|
||||
(Pyupgrade, "003") => Rule::TypeOfPrimitive,
|
||||
(Pyupgrade, "004") => Rule::UselessObjectInheritance,
|
||||
(Pyupgrade, "005") => Rule::DeprecatedUnittestAlias,
|
||||
(Pyupgrade, "006") => Rule::DeprecatedCollectionType,
|
||||
(Pyupgrade, "007") => Rule::TypingUnion,
|
||||
(Pyupgrade, "008") => Rule::SuperCallWithParameters,
|
||||
(Pyupgrade, "009") => Rule::UTF8EncodingDeclaration,
|
||||
(Pyupgrade, "010") => Rule::UnnecessaryFutureImport,
|
||||
(Pyupgrade, "011") => Rule::LRUCacheWithoutParameters,
|
||||
(Pyupgrade, "012") => Rule::UnnecessaryEncodeUTF8,
|
||||
(Pyupgrade, "013") => Rule::ConvertTypedDictFunctionalToClass,
|
||||
(Pyupgrade, "014") => Rule::ConvertNamedTupleFunctionalToClass,
|
||||
(Pyupgrade, "015") => Rule::RedundantOpenModes,
|
||||
(Pyupgrade, "017") => Rule::DatetimeTimezoneUTC,
|
||||
(Pyupgrade, "018") => Rule::NativeLiterals,
|
||||
(Pyupgrade, "019") => Rule::TypingTextStrAlias,
|
||||
(Pyupgrade, "020") => Rule::OpenAlias,
|
||||
(Pyupgrade, "021") => Rule::ReplaceUniversalNewlines,
|
||||
(Pyupgrade, "022") => Rule::ReplaceStdoutStderr,
|
||||
(Pyupgrade, "023") => Rule::RewriteCElementTree,
|
||||
(Pyupgrade, "024") => Rule::OSErrorAlias,
|
||||
(Pyupgrade, "025") => Rule::RewriteUnicodeLiteral,
|
||||
(Pyupgrade, "026") => Rule::RewriteMockImport,
|
||||
(Pyupgrade, "027") => Rule::RewriteListComprehension,
|
||||
(Pyupgrade, "028") => Rule::RewriteYieldFrom,
|
||||
(Pyupgrade, "029") => Rule::UnnecessaryBuiltinImport,
|
||||
(Pyupgrade, "030") => Rule::FormatLiterals,
|
||||
(Pyupgrade, "031") => Rule::PrintfStringFormatting,
|
||||
(Pyupgrade, "032") => Rule::FString,
|
||||
(Pyupgrade, "033") => Rule::FunctoolsCache,
|
||||
(Pyupgrade, "034") => Rule::ExtraneousParentheses,
|
||||
(Pyupgrade, "035") => Rule::ImportReplacements,
|
||||
(Pyupgrade, "036") => Rule::OutdatedVersionBlock,
|
||||
(Pyupgrade, "037") => Rule::QuotedAnnotation,
|
||||
|
||||
// pydocstyle
|
||||
(Pydocstyle, "100") => Rule::PublicModule,
|
||||
(Pydocstyle, "101") => Rule::PublicClass,
|
||||
(Pydocstyle, "102") => Rule::PublicMethod,
|
||||
(Pydocstyle, "103") => Rule::PublicFunction,
|
||||
(Pydocstyle, "104") => Rule::PublicPackage,
|
||||
(Pydocstyle, "105") => Rule::MagicMethod,
|
||||
(Pydocstyle, "106") => Rule::PublicNestedClass,
|
||||
(Pydocstyle, "107") => Rule::PublicInit,
|
||||
(Pydocstyle, "200") => Rule::FitsOnOneLine,
|
||||
(Pydocstyle, "201") => Rule::NoBlankLineBeforeFunction,
|
||||
(Pydocstyle, "202") => Rule::NoBlankLineAfterFunction,
|
||||
(Pydocstyle, "203") => Rule::OneBlankLineBeforeClass,
|
||||
(Pydocstyle, "204") => Rule::OneBlankLineAfterClass,
|
||||
(Pydocstyle, "205") => Rule::BlankLineAfterSummary,
|
||||
(Pydocstyle, "206") => Rule::IndentWithSpaces,
|
||||
(Pydocstyle, "207") => Rule::NoUnderIndentation,
|
||||
(Pydocstyle, "208") => Rule::NoOverIndentation,
|
||||
(Pydocstyle, "209") => Rule::NewLineAfterLastParagraph,
|
||||
(Pydocstyle, "210") => Rule::NoSurroundingWhitespace,
|
||||
(Pydocstyle, "211") => Rule::NoBlankLineBeforeClass,
|
||||
(Pydocstyle, "212") => Rule::MultiLineSummaryFirstLine,
|
||||
(Pydocstyle, "213") => Rule::MultiLineSummarySecondLine,
|
||||
(Pydocstyle, "214") => Rule::SectionNotOverIndented,
|
||||
(Pydocstyle, "215") => Rule::SectionUnderlineNotOverIndented,
|
||||
(Pydocstyle, "300") => Rule::TripleSingleQuotes,
|
||||
(Pydocstyle, "301") => Rule::EscapeSequenceInDocstring,
|
||||
(Pydocstyle, "400") => Rule::EndsInPeriod,
|
||||
(Pydocstyle, "401") => Rule::NonImperativeMood,
|
||||
(Pydocstyle, "402") => Rule::NoSignature,
|
||||
(Pydocstyle, "403") => Rule::FirstLineCapitalized,
|
||||
(Pydocstyle, "404") => Rule::DocstringStartsWithThis,
|
||||
(Pydocstyle, "405") => Rule::CapitalizeSectionName,
|
||||
(Pydocstyle, "406") => Rule::NewLineAfterSectionName,
|
||||
(Pydocstyle, "407") => Rule::DashedUnderlineAfterSection,
|
||||
(Pydocstyle, "408") => Rule::SectionUnderlineAfterName,
|
||||
(Pydocstyle, "409") => Rule::SectionUnderlineMatchesSectionLength,
|
||||
(Pydocstyle, "410") => Rule::BlankLineAfterSection,
|
||||
(Pydocstyle, "411") => Rule::BlankLineBeforeSection,
|
||||
(Pydocstyle, "412") => Rule::NoBlankLinesBetweenHeaderAndContent,
|
||||
(Pydocstyle, "413") => Rule::BlankLineAfterLastSection,
|
||||
(Pydocstyle, "414") => Rule::EmptyDocstringSection,
|
||||
(Pydocstyle, "415") => Rule::EndsInPunctuation,
|
||||
(Pydocstyle, "416") => Rule::SectionNameEndsInColon,
|
||||
(Pydocstyle, "417") => Rule::UndocumentedParam,
|
||||
(Pydocstyle, "418") => Rule::OverloadWithDocstring,
|
||||
(Pydocstyle, "419") => Rule::EmptyDocstring,
|
||||
|
||||
// pep8-naming
|
||||
(PEP8Naming, "801") => Rule::InvalidClassName,
|
||||
(PEP8Naming, "802") => Rule::InvalidFunctionName,
|
||||
(PEP8Naming, "803") => Rule::InvalidArgumentName,
|
||||
(PEP8Naming, "804") => Rule::InvalidFirstArgumentNameForClassMethod,
|
||||
(PEP8Naming, "805") => Rule::InvalidFirstArgumentNameForMethod,
|
||||
(PEP8Naming, "806") => Rule::NonLowercaseVariableInFunction,
|
||||
(PEP8Naming, "807") => Rule::DunderFunctionName,
|
||||
(PEP8Naming, "811") => Rule::ConstantImportedAsNonConstant,
|
||||
(PEP8Naming, "812") => Rule::LowercaseImportedAsNonLowercase,
|
||||
(PEP8Naming, "813") => Rule::CamelcaseImportedAsLowercase,
|
||||
(PEP8Naming, "814") => Rule::CamelcaseImportedAsConstant,
|
||||
(PEP8Naming, "815") => Rule::MixedCaseVariableInClassScope,
|
||||
(PEP8Naming, "816") => Rule::MixedCaseVariableInGlobalScope,
|
||||
(PEP8Naming, "817") => Rule::CamelcaseImportedAsAcronym,
|
||||
(PEP8Naming, "818") => Rule::ErrorSuffixOnExceptionName,
|
||||
|
||||
// isort
|
||||
(Isort, "001") => Rule::UnsortedImports,
|
||||
(Isort, "002") => Rule::MissingRequiredImport,
|
||||
|
||||
// eradicate
|
||||
(Eradicate, "001") => Rule::CommentedOutCode,
|
||||
|
||||
// flake8-bandit
|
||||
(Flake8Bandit, "101") => Rule::Assert,
|
||||
(Flake8Bandit, "102") => Rule::ExecBuiltin,
|
||||
(Flake8Bandit, "103") => Rule::BadFilePermissions,
|
||||
(Flake8Bandit, "104") => Rule::HardcodedBindAllInterfaces,
|
||||
(Flake8Bandit, "105") => Rule::HardcodedPasswordString,
|
||||
(Flake8Bandit, "106") => Rule::HardcodedPasswordFuncArg,
|
||||
(Flake8Bandit, "107") => Rule::HardcodedPasswordDefault,
|
||||
(Flake8Bandit, "608") => Rule::HardcodedSQLExpression,
|
||||
(Flake8Bandit, "108") => Rule::HardcodedTempFile,
|
||||
(Flake8Bandit, "110") => Rule::TryExceptPass,
|
||||
(Flake8Bandit, "112") => Rule::TryExceptContinue,
|
||||
(Flake8Bandit, "113") => Rule::RequestWithoutTimeout,
|
||||
(Flake8Bandit, "324") => Rule::HashlibInsecureHashFunction,
|
||||
(Flake8Bandit, "501") => Rule::RequestWithNoCertValidation,
|
||||
(Flake8Bandit, "506") => Rule::UnsafeYAMLLoad,
|
||||
(Flake8Bandit, "508") => Rule::SnmpInsecureVersion,
|
||||
(Flake8Bandit, "509") => Rule::SnmpWeakCryptography,
|
||||
(Flake8Bandit, "612") => Rule::LoggingConfigInsecureListen,
|
||||
(Flake8Bandit, "701") => Rule::Jinja2AutoescapeFalse,
|
||||
|
||||
// flake8-boolean-trap
|
||||
(Flake8BooleanTrap, "001") => Rule::BooleanPositionalArgInFunctionDefinition,
|
||||
(Flake8BooleanTrap, "002") => Rule::BooleanDefaultValueInFunctionDefinition,
|
||||
(Flake8BooleanTrap, "003") => Rule::BooleanPositionalValueInFunctionCall,
|
||||
|
||||
// flake8-unused-arguments
|
||||
(Flake8UnusedArguments, "001") => Rule::UnusedFunctionArgument,
|
||||
(Flake8UnusedArguments, "002") => Rule::UnusedMethodArgument,
|
||||
(Flake8UnusedArguments, "003") => Rule::UnusedClassMethodArgument,
|
||||
(Flake8UnusedArguments, "004") => Rule::UnusedStaticMethodArgument,
|
||||
(Flake8UnusedArguments, "005") => Rule::UnusedLambdaArgument,
|
||||
|
||||
// flake8-import-conventions
|
||||
(Flake8ImportConventions, "001") => Rule::UnconventionalImportAlias,
|
||||
|
||||
// flake8-datetimez
|
||||
(Flake8Datetimez, "001") => Rule::CallDatetimeWithoutTzinfo,
|
||||
(Flake8Datetimez, "002") => Rule::CallDatetimeToday,
|
||||
(Flake8Datetimez, "003") => Rule::CallDatetimeUtcnow,
|
||||
(Flake8Datetimez, "004") => Rule::CallDatetimeUtcfromtimestamp,
|
||||
(Flake8Datetimez, "005") => Rule::CallDatetimeNowWithoutTzinfo,
|
||||
(Flake8Datetimez, "006") => Rule::CallDatetimeFromtimestamp,
|
||||
(Flake8Datetimez, "007") => Rule::CallDatetimeStrptimeWithoutZone,
|
||||
(Flake8Datetimez, "011") => Rule::CallDateToday,
|
||||
(Flake8Datetimez, "012") => Rule::CallDateFromtimestamp,
|
||||
|
||||
// pygrep-hooks
|
||||
(PygrepHooks, "001") => Rule::NoEval,
|
||||
(PygrepHooks, "002") => Rule::DeprecatedLogWarn,
|
||||
(PygrepHooks, "003") => Rule::BlanketTypeIgnore,
|
||||
(PygrepHooks, "004") => Rule::BlanketNOQA,
|
||||
|
||||
// pandas-vet
|
||||
(PandasVet, "002") => Rule::UseOfInplaceArgument,
|
||||
(PandasVet, "003") => Rule::UseOfDotIsNull,
|
||||
(PandasVet, "004") => Rule::UseOfDotNotNull,
|
||||
(PandasVet, "007") => Rule::UseOfDotIx,
|
||||
(PandasVet, "008") => Rule::UseOfDotAt,
|
||||
(PandasVet, "009") => Rule::UseOfDotIat,
|
||||
(PandasVet, "010") => Rule::UseOfDotPivotOrUnstack,
|
||||
(PandasVet, "011") => Rule::UseOfDotValues,
|
||||
(PandasVet, "012") => Rule::UseOfDotReadTable,
|
||||
(PandasVet, "013") => Rule::UseOfDotStack,
|
||||
(PandasVet, "015") => Rule::UseOfPdMerge,
|
||||
(PandasVet, "901") => Rule::DfIsABadVariableName,
|
||||
|
||||
// flake8-errmsg
|
||||
(Flake8ErrMsg, "101") => Rule::RawStringInException,
|
||||
(Flake8ErrMsg, "102") => Rule::FStringInException,
|
||||
(Flake8ErrMsg, "103") => Rule::DotFormatInException,
|
||||
|
||||
// flake8-pyi
|
||||
(Flake8Pyi, "001") => Rule::PrefixTypeParams,
|
||||
(Flake8Pyi, "007") => Rule::UnrecognizedPlatformCheck,
|
||||
(Flake8Pyi, "008") => Rule::UnrecognizedPlatformName,
|
||||
|
||||
// flake8-pytest-style
|
||||
(Flake8PytestStyle, "001") => Rule::IncorrectFixtureParenthesesStyle,
|
||||
(Flake8PytestStyle, "002") => Rule::FixturePositionalArgs,
|
||||
(Flake8PytestStyle, "003") => Rule::ExtraneousScopeFunction,
|
||||
(Flake8PytestStyle, "004") => Rule::MissingFixtureNameUnderscore,
|
||||
(Flake8PytestStyle, "005") => Rule::IncorrectFixtureNameUnderscore,
|
||||
(Flake8PytestStyle, "006") => Rule::ParametrizeNamesWrongType,
|
||||
(Flake8PytestStyle, "007") => Rule::ParametrizeValuesWrongType,
|
||||
(Flake8PytestStyle, "008") => Rule::PatchWithLambda,
|
||||
(Flake8PytestStyle, "009") => Rule::UnittestAssertion,
|
||||
(Flake8PytestStyle, "010") => Rule::RaisesWithoutException,
|
||||
(Flake8PytestStyle, "011") => Rule::RaisesTooBroad,
|
||||
(Flake8PytestStyle, "012") => Rule::RaisesWithMultipleStatements,
|
||||
(Flake8PytestStyle, "013") => Rule::IncorrectPytestImport,
|
||||
(Flake8PytestStyle, "015") => Rule::AssertAlwaysFalse,
|
||||
(Flake8PytestStyle, "016") => Rule::FailWithoutMessage,
|
||||
(Flake8PytestStyle, "017") => Rule::AssertInExcept,
|
||||
(Flake8PytestStyle, "018") => Rule::CompositeAssertion,
|
||||
(Flake8PytestStyle, "019") => Rule::FixtureParamWithoutValue,
|
||||
(Flake8PytestStyle, "020") => Rule::DeprecatedYieldFixture,
|
||||
(Flake8PytestStyle, "021") => Rule::FixtureFinalizerCallback,
|
||||
(Flake8PytestStyle, "022") => Rule::UselessYieldFixture,
|
||||
(Flake8PytestStyle, "023") => Rule::IncorrectMarkParenthesesStyle,
|
||||
(Flake8PytestStyle, "024") => Rule::UnnecessaryAsyncioMarkOnFixture,
|
||||
(Flake8PytestStyle, "025") => Rule::ErroneousUseFixturesOnFixture,
|
||||
(Flake8PytestStyle, "026") => Rule::UseFixturesWithoutParameters,
|
||||
|
||||
// flake8-pie
|
||||
(Flake8Pie, "790") => Rule::UnnecessaryPass,
|
||||
(Flake8Pie, "794") => Rule::DupeClassFieldDefinitions,
|
||||
(Flake8Pie, "796") => Rule::PreferUniqueEnums,
|
||||
(Flake8Pie, "800") => Rule::UnnecessarySpread,
|
||||
(Flake8Pie, "804") => Rule::UnnecessaryDictKwargs,
|
||||
(Flake8Pie, "807") => Rule::PreferListBuiltin,
|
||||
(Flake8Pie, "810") => Rule::SingleStartsEndsWith,
|
||||
|
||||
// flake8-commas
|
||||
(Flake8Commas, "812") => Rule::TrailingCommaMissing,
|
||||
(Flake8Commas, "818") => Rule::TrailingCommaOnBareTupleProhibited,
|
||||
(Flake8Commas, "819") => Rule::TrailingCommaProhibited,
|
||||
|
||||
// flake8-no-pep420
|
||||
(Flake8NoPep420, "001") => Rule::ImplicitNamespacePackage,
|
||||
|
||||
// flake8-executable
|
||||
(Flake8Executable, "001") => Rule::ShebangNotExecutable,
|
||||
(Flake8Executable, "002") => Rule::ShebangMissingExecutableFile,
|
||||
(Flake8Executable, "003") => Rule::ShebangPython,
|
||||
(Flake8Executable, "004") => Rule::ShebangWhitespace,
|
||||
(Flake8Executable, "005") => Rule::ShebangNewline,
|
||||
|
||||
// flake8-type-checking
|
||||
(Flake8TypeChecking, "001") => Rule::TypingOnlyFirstPartyImport,
|
||||
(Flake8TypeChecking, "002") => Rule::TypingOnlyThirdPartyImport,
|
||||
(Flake8TypeChecking, "003") => Rule::TypingOnlyStandardLibraryImport,
|
||||
(Flake8TypeChecking, "004") => Rule::RuntimeImportInTypeCheckingBlock,
|
||||
(Flake8TypeChecking, "005") => Rule::EmptyTypeCheckingBlock,
|
||||
|
||||
// tryceratops
|
||||
(Tryceratops, "002") => Rule::RaiseVanillaClass,
|
||||
(Tryceratops, "003") => Rule::RaiseVanillaArgs,
|
||||
(Tryceratops, "004") => Rule::PreferTypeError,
|
||||
(Tryceratops, "200") => Rule::ReraiseNoCause,
|
||||
(Tryceratops, "201") => Rule::VerboseRaise,
|
||||
(Tryceratops, "300") => Rule::TryConsiderElse,
|
||||
(Tryceratops, "301") => Rule::RaiseWithinTry,
|
||||
(Tryceratops, "400") => Rule::ErrorInsteadOfException,
|
||||
|
||||
// flake8-use-pathlib
|
||||
(Flake8UsePathlib, "100") => Rule::PathlibAbspath,
|
||||
(Flake8UsePathlib, "101") => Rule::PathlibChmod,
|
||||
(Flake8UsePathlib, "102") => Rule::PathlibMkdir,
|
||||
(Flake8UsePathlib, "103") => Rule::PathlibMakedirs,
|
||||
(Flake8UsePathlib, "104") => Rule::PathlibRename,
|
||||
(Flake8UsePathlib, "105") => Rule::PathlibReplace,
|
||||
(Flake8UsePathlib, "106") => Rule::PathlibRmdir,
|
||||
(Flake8UsePathlib, "107") => Rule::PathlibRemove,
|
||||
(Flake8UsePathlib, "108") => Rule::PathlibUnlink,
|
||||
(Flake8UsePathlib, "109") => Rule::PathlibGetcwd,
|
||||
(Flake8UsePathlib, "110") => Rule::PathlibExists,
|
||||
(Flake8UsePathlib, "111") => Rule::PathlibExpanduser,
|
||||
(Flake8UsePathlib, "112") => Rule::PathlibIsDir,
|
||||
(Flake8UsePathlib, "113") => Rule::PathlibIsFile,
|
||||
(Flake8UsePathlib, "114") => Rule::PathlibIsLink,
|
||||
(Flake8UsePathlib, "115") => Rule::PathlibReadlink,
|
||||
(Flake8UsePathlib, "116") => Rule::PathlibStat,
|
||||
(Flake8UsePathlib, "117") => Rule::PathlibIsAbs,
|
||||
(Flake8UsePathlib, "118") => Rule::PathlibJoin,
|
||||
(Flake8UsePathlib, "119") => Rule::PathlibBasename,
|
||||
(Flake8UsePathlib, "120") => Rule::PathlibDirname,
|
||||
(Flake8UsePathlib, "121") => Rule::PathlibSamefile,
|
||||
(Flake8UsePathlib, "122") => Rule::PathlibSplitext,
|
||||
(Flake8UsePathlib, "123") => Rule::PathlibOpen,
|
||||
(Flake8UsePathlib, "124") => Rule::PathlibPyPath,
|
||||
|
||||
// flake8-logging-format
|
||||
(Flake8LoggingFormat, "001") => Rule::LoggingStringFormat,
|
||||
(Flake8LoggingFormat, "002") => Rule::LoggingPercentFormat,
|
||||
(Flake8LoggingFormat, "003") => Rule::LoggingStringConcat,
|
||||
(Flake8LoggingFormat, "004") => Rule::LoggingFString,
|
||||
(Flake8LoggingFormat, "010") => Rule::LoggingWarn,
|
||||
(Flake8LoggingFormat, "101") => Rule::LoggingExtraAttrClash,
|
||||
(Flake8LoggingFormat, "201") => Rule::LoggingExcInfo,
|
||||
(Flake8LoggingFormat, "202") => Rule::LoggingRedundantExcInfo,
|
||||
|
||||
// flake8-raise
|
||||
(Flake8Raise, "102") => Rule::UnnecessaryParenOnRaiseException,
|
||||
|
||||
// flake8-self
|
||||
(Flake8Self, "001") => Rule::PrivateMemberAccess,
|
||||
|
||||
// numpy
|
||||
(Numpy, "001") => Rule::NumpyDeprecatedTypeAlias,
|
||||
|
||||
// ruff
|
||||
(Ruff, "001") => Rule::AmbiguousUnicodeCharacterString,
|
||||
(Ruff, "002") => Rule::AmbiguousUnicodeCharacterDocstring,
|
||||
(Ruff, "003") => Rule::AmbiguousUnicodeCharacterComment,
|
||||
(Ruff, "004") => Rule::KeywordArgumentBeforeStarArgument,
|
||||
(Ruff, "005") => Rule::UnpackInsteadOfConcatenatingToCollectionLiteral,
|
||||
(Ruff, "006") => Rule::AsyncioDanglingTask,
|
||||
(Ruff, "100") => Rule::UnusedNOQA,
|
||||
|
||||
// flake8-django
|
||||
(Flake8Django, "001") => Rule::NullableModelStringField,
|
||||
(Flake8Django, "008") => Rule::ModelWithoutDunderStr,
|
||||
(Flake8Django, "013") => Rule::NonLeadingReceiverDecorator,
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -59,6 +59,7 @@ pub fn extract_directives(lxr: &[LexResult], flags: Flags) -> Directives {
|
||||
/// Extract a mapping from logical line to noqa line.
|
||||
pub fn extract_noqa_line_for(lxr: &[LexResult]) -> IntMap<usize, usize> {
|
||||
let mut noqa_line_for: IntMap<usize, usize> = IntMap::default();
|
||||
let mut prev_non_newline: Option<(&Location, &Tok, &Location)> = None;
|
||||
for (start, tok, end) in lxr.iter().flatten() {
|
||||
if matches!(tok, Tok::EndOfFile) {
|
||||
break;
|
||||
@@ -70,6 +71,21 @@ pub fn extract_noqa_line_for(lxr: &[LexResult]) -> IntMap<usize, usize> {
|
||||
noqa_line_for.insert(i, end.row());
|
||||
}
|
||||
}
|
||||
// For continuations, we expect `noqa` directives on the last line of the
|
||||
// continuation.
|
||||
if matches!(
|
||||
tok,
|
||||
Tok::Newline | Tok::NonLogicalNewline | Tok::Comment(..)
|
||||
) {
|
||||
if let Some((.., end)) = prev_non_newline {
|
||||
for i in end.row()..start.row() {
|
||||
noqa_line_for.insert(i, start.row());
|
||||
}
|
||||
}
|
||||
prev_non_newline = None;
|
||||
} else if prev_non_newline.is_none() {
|
||||
prev_non_newline = Some((start, tok, end));
|
||||
}
|
||||
}
|
||||
noqa_line_for
|
||||
}
|
||||
@@ -193,11 +209,11 @@ z = x + 1",
|
||||
|
||||
let lxr: Vec<LexResult> = lexer::make_tokenizer(
|
||||
"x = 1
|
||||
y = '''abc
|
||||
def
|
||||
ghi
|
||||
'''
|
||||
z = 2",
|
||||
y = '''abc
|
||||
def
|
||||
ghi
|
||||
'''
|
||||
z = 2",
|
||||
)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
@@ -207,16 +223,51 @@ z = x + 1",
|
||||
|
||||
let lxr: Vec<LexResult> = lexer::make_tokenizer(
|
||||
"x = 1
|
||||
y = '''abc
|
||||
def
|
||||
ghi
|
||||
'''",
|
||||
y = '''abc
|
||||
def
|
||||
ghi
|
||||
'''",
|
||||
)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
extract_noqa_line_for(&lxr),
|
||||
IntMap::from_iter([(2, 5), (3, 5), (4, 5)])
|
||||
);
|
||||
|
||||
let lxr: Vec<LexResult> = lexer::make_tokenizer(
|
||||
r#"x = \
|
||||
1"#,
|
||||
)
|
||||
.collect();
|
||||
assert_eq!(extract_noqa_line_for(&lxr), IntMap::from_iter([(1, 2)]));
|
||||
|
||||
let lxr: Vec<LexResult> = lexer::make_tokenizer(
|
||||
r#"from foo import \
|
||||
bar as baz, \
|
||||
qux as quux"#,
|
||||
)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
extract_noqa_line_for(&lxr),
|
||||
IntMap::from_iter([(1, 3), (2, 3)])
|
||||
);
|
||||
|
||||
let lxr: Vec<LexResult> = lexer::make_tokenizer(
|
||||
r#"
|
||||
# Foo
|
||||
from foo import \
|
||||
bar as baz, \
|
||||
qux as quux # Baz
|
||||
x = \
|
||||
1
|
||||
y = \
|
||||
2"#,
|
||||
)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
extract_noqa_line_for(&lxr),
|
||||
IntMap::from_iter([(3, 5), (4, 5), (6, 7), (8, 9)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -6,8 +6,8 @@ use itertools::Itertools;
|
||||
use super::external_config::ExternalConfig;
|
||||
use super::plugin::Plugin;
|
||||
use super::{parser, plugin};
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::rule_selector::{prefix_to_selector, RuleSelector};
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::rules::flake8_pytest_style::types::{
|
||||
ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType,
|
||||
};
|
||||
@@ -23,9 +23,8 @@ use crate::settings::pyproject::Pyproject;
|
||||
use crate::warn_user;
|
||||
|
||||
const DEFAULT_SELECTORS: &[RuleSelector] = &[
|
||||
prefix_to_selector(RuleCodePrefix::F),
|
||||
prefix_to_selector(RuleCodePrefix::E),
|
||||
prefix_to_selector(RuleCodePrefix::W),
|
||||
RuleSelector::Linter(Linter::Pyflakes),
|
||||
RuleSelector::Linter(Linter::Pycodestyle),
|
||||
];
|
||||
|
||||
pub fn convert(
|
||||
@@ -359,13 +358,13 @@ pub fn convert(
|
||||
options.select = Some(
|
||||
select
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
);
|
||||
options.ignore = Some(
|
||||
ignore
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
);
|
||||
if flake8_annotations != flake8_annotations::settings::Options::default() {
|
||||
@@ -433,7 +432,7 @@ pub fn convert(
|
||||
/// plugins.
|
||||
fn resolve_select(plugins: &[Plugin]) -> HashSet<RuleSelector> {
|
||||
let mut select: HashSet<_> = DEFAULT_SELECTORS.iter().cloned().collect();
|
||||
select.extend(plugins.iter().map(Plugin::selector));
|
||||
select.extend(plugins.iter().map(|p| Linter::from(p).into()));
|
||||
select
|
||||
}
|
||||
|
||||
@@ -448,7 +447,7 @@ mod tests {
|
||||
use super::convert;
|
||||
use crate::flake8_to_ruff::converter::DEFAULT_SELECTORS;
|
||||
use crate::flake8_to_ruff::ExternalConfig;
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
use crate::rules::{flake8_quotes, pydocstyle};
|
||||
@@ -463,7 +462,7 @@ mod tests {
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(plugins)
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
),
|
||||
..Options::default()
|
||||
@@ -578,7 +577,7 @@ mod tests {
|
||||
pydocstyle: Some(pydocstyle::settings::Options {
|
||||
convention: Some(Convention::Numpy),
|
||||
}),
|
||||
..default_options([RuleCodePrefix::D.into()])
|
||||
..default_options([Linter::Pydocstyle.into()])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -602,7 +601,7 @@ mod tests {
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..default_options([RuleCodePrefix::Q.into()])
|
||||
..default_options([Linter::Flake8Quotes.into()])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
|
||||
@@ -196,7 +196,8 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use super::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::codes;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::settings::types::PatternPrefixPair;
|
||||
|
||||
@@ -211,19 +212,25 @@ mod tests {
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401");
|
||||
let expected = vec![RuleCodePrefix::F401.into()];
|
||||
let expected = vec![codes::Pyflakes::_401.into()];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401,");
|
||||
let expected = vec![RuleCodePrefix::F401.into()];
|
||||
let expected = vec![codes::Pyflakes::_401.into()];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401,E501");
|
||||
let expected = vec![RuleCodePrefix::F401.into(), RuleCodePrefix::E501.into()];
|
||||
let expected = vec![
|
||||
codes::Pyflakes::_401.into(),
|
||||
codes::Pycodestyle::E501.into(),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401, E501");
|
||||
let expected = vec![RuleCodePrefix::F401.into(), RuleCodePrefix::E501.into()];
|
||||
let expected = vec![
|
||||
codes::Pyflakes::_401.into(),
|
||||
codes::Pycodestyle::E501.into(),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -276,11 +283,11 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "locust/test/*".to_string(),
|
||||
prefix: RuleCodePrefix::F841.into(),
|
||||
prefix: codes::Pyflakes::_841.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "examples/*".to_string(),
|
||||
prefix: RuleCodePrefix::F841.into(),
|
||||
prefix: codes::Pyflakes::_841.into(),
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
@@ -296,23 +303,23 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "t/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "setup.py".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "examples/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "docs/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "extra/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
@@ -334,47 +341,47 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402.into(),
|
||||
prefix: codes::Pycodestyle::E402.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/core/downloader/handlers/http.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/http/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/linkextractors/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402.into(),
|
||||
prefix: codes::Pycodestyle::E402.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/linkextractors/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/selector/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/spiders/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402.into(),
|
||||
prefix: codes::Pycodestyle::E402.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/spiders/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/utils/url.py".to_string(),
|
||||
prefix: RuleCodePrefix::F403.into(),
|
||||
prefix: codes::Pyflakes::_403.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/utils/url.py".to_string(),
|
||||
prefix: RuleCodePrefix::F405.into(),
|
||||
prefix: codes::Pyflakes::_405.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "tests/test_loader.py".to_string(),
|
||||
prefix: RuleCodePrefix::E741.into(),
|
||||
prefix: codes::Pycodestyle::E741.into(),
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
|
||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
@@ -131,43 +131,42 @@ impl fmt::Debug for Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(martin): Convert into `impl From<Plugin> for Linter`
|
||||
impl Plugin {
|
||||
pub fn selector(&self) -> RuleSelector {
|
||||
match self {
|
||||
Plugin::Flake82020 => RuleCodePrefix::YTT.into(),
|
||||
Plugin::Flake8Annotations => RuleCodePrefix::ANN.into(),
|
||||
Plugin::Flake8Bandit => RuleCodePrefix::S.into(),
|
||||
Plugin::Flake8BlindExcept => RuleCodePrefix::BLE.into(),
|
||||
Plugin::Flake8BooleanTrap => RuleCodePrefix::FBT.into(),
|
||||
Plugin::Flake8Bugbear => RuleCodePrefix::B.into(),
|
||||
Plugin::Flake8Builtins => RuleCodePrefix::A.into(),
|
||||
Plugin::Flake8Commas => RuleCodePrefix::COM.into(),
|
||||
Plugin::Flake8Comprehensions => RuleCodePrefix::C4.into(),
|
||||
Plugin::Flake8Datetimez => RuleCodePrefix::DTZ.into(),
|
||||
Plugin::Flake8Debugger => RuleCodePrefix::T1.into(),
|
||||
Plugin::Flake8Docstrings => RuleCodePrefix::D.into(),
|
||||
Plugin::Flake8Eradicate => RuleCodePrefix::ERA.into(),
|
||||
Plugin::Flake8ErrMsg => RuleCodePrefix::EM.into(),
|
||||
Plugin::Flake8Executable => RuleCodePrefix::EXE.into(),
|
||||
Plugin::Flake8ImplicitStrConcat => RuleCodePrefix::ISC.into(),
|
||||
Plugin::Flake8ImportConventions => RuleCodePrefix::ICN.into(),
|
||||
Plugin::Flake8NoPep420 => RuleCodePrefix::INP.into(),
|
||||
Plugin::Flake8Pie => RuleCodePrefix::PIE.into(),
|
||||
Plugin::Flake8Print => RuleCodePrefix::T2.into(),
|
||||
Plugin::Flake8PytestStyle => RuleCodePrefix::PT.into(),
|
||||
Plugin::Flake8Quotes => RuleCodePrefix::Q.into(),
|
||||
Plugin::Flake8Return => RuleCodePrefix::RET.into(),
|
||||
Plugin::Flake8Simplify => RuleCodePrefix::SIM.into(),
|
||||
Plugin::Flake8TidyImports => RuleCodePrefix::TID.into(),
|
||||
Plugin::Flake8TypeChecking => RuleCodePrefix::TCH.into(),
|
||||
Plugin::Flake8UnusedArguments => RuleCodePrefix::ARG.into(),
|
||||
Plugin::Flake8UsePathlib => RuleCodePrefix::PTH.into(),
|
||||
Plugin::McCabe => RuleCodePrefix::C9.into(),
|
||||
Plugin::PEP8Naming => RuleCodePrefix::N.into(),
|
||||
Plugin::PandasVet => RuleCodePrefix::PD.into(),
|
||||
Plugin::Pyupgrade => RuleCodePrefix::UP.into(),
|
||||
Plugin::Tryceratops => RuleCodePrefix::TRY.into(),
|
||||
impl From<&Plugin> for Linter {
|
||||
fn from(plugin: &Plugin) -> Self {
|
||||
match plugin {
|
||||
Plugin::Flake82020 => Linter::Flake82020,
|
||||
Plugin::Flake8Annotations => Linter::Flake8Annotations,
|
||||
Plugin::Flake8Bandit => Linter::Flake8Bandit,
|
||||
Plugin::Flake8BlindExcept => Linter::Flake8BlindExcept,
|
||||
Plugin::Flake8BooleanTrap => Linter::Flake8BooleanTrap,
|
||||
Plugin::Flake8Bugbear => Linter::Flake8Bugbear,
|
||||
Plugin::Flake8Builtins => Linter::Flake8Builtins,
|
||||
Plugin::Flake8Commas => Linter::Flake8Commas,
|
||||
Plugin::Flake8Comprehensions => Linter::Flake8Comprehensions,
|
||||
Plugin::Flake8Datetimez => Linter::Flake8Datetimez,
|
||||
Plugin::Flake8Debugger => Linter::Flake8Debugger,
|
||||
Plugin::Flake8Docstrings => Linter::Pydocstyle,
|
||||
Plugin::Flake8Eradicate => Linter::Eradicate,
|
||||
Plugin::Flake8ErrMsg => Linter::Flake8ErrMsg,
|
||||
Plugin::Flake8Executable => Linter::Flake8Executable,
|
||||
Plugin::Flake8ImplicitStrConcat => Linter::Flake8ImplicitStrConcat,
|
||||
Plugin::Flake8ImportConventions => Linter::Flake8ImportConventions,
|
||||
Plugin::Flake8NoPep420 => Linter::Flake8NoPep420,
|
||||
Plugin::Flake8Pie => Linter::Flake8Pie,
|
||||
Plugin::Flake8Print => Linter::Flake8Print,
|
||||
Plugin::Flake8PytestStyle => Linter::Flake8PytestStyle,
|
||||
Plugin::Flake8Quotes => Linter::Flake8Quotes,
|
||||
Plugin::Flake8Return => Linter::Flake8Return,
|
||||
Plugin::Flake8Simplify => Linter::Flake8Simplify,
|
||||
Plugin::Flake8TidyImports => Linter::Flake8TidyImports,
|
||||
Plugin::Flake8TypeChecking => Linter::Flake8TypeChecking,
|
||||
Plugin::Flake8UnusedArguments => Linter::Flake8UnusedArguments,
|
||||
Plugin::Flake8UsePathlib => Linter::Flake8UsePathlib,
|
||||
Plugin::McCabe => Linter::McCabe,
|
||||
Plugin::PEP8Naming => Linter::PEP8Naming,
|
||||
Plugin::PandasVet => Linter::PandasVet,
|
||||
Plugin::Pyupgrade => Linter::Pyupgrade,
|
||||
Plugin::Tryceratops => Linter::Tryceratops,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +333,7 @@ pub fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec<Plugin
|
||||
for selector in selectors {
|
||||
if selector
|
||||
.into_iter()
|
||||
.any(|rule| plugin.selector().into_iter().any(|r| r == rule))
|
||||
.any(|rule| Linter::from(plugin).into_iter().any(|r| r == rule))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Default)]
|
||||
enum State {
|
||||
// Start of the module: first string gets marked as a docstring.
|
||||
#[default]
|
||||
ExpectModuleDocstring,
|
||||
// After seeing a class definition, we're waiting for the block colon (and do bracket
|
||||
// counting).
|
||||
@@ -23,25 +24,13 @@ enum State {
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StateMachine {
|
||||
state: State,
|
||||
bracket_count: usize,
|
||||
}
|
||||
|
||||
impl Default for StateMachine {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl StateMachine {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: State::ExpectModuleDocstring,
|
||||
bracket_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, tok: &Tok) -> bool {
|
||||
if matches!(
|
||||
tok,
|
||||
|
||||
@@ -5,11 +5,17 @@
|
||||
//!
|
||||
//! [Ruff]: https://github.com/charliermarsh/ruff
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
pub use rule_selector::RuleSelector;
|
||||
pub use rules::pycodestyle::rules::IOError;
|
||||
pub use violation::{AutofixKind, Availability as AutofixAvailability};
|
||||
|
||||
mod assert_yaml_snapshot;
|
||||
mod ast;
|
||||
mod autofix;
|
||||
pub mod cache;
|
||||
mod checkers;
|
||||
mod codes;
|
||||
mod cst;
|
||||
mod directives;
|
||||
mod doc_lines;
|
||||
@@ -34,20 +40,12 @@ mod vendor;
|
||||
mod violation;
|
||||
mod visibility;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
pub use rule_selector::RuleSelector;
|
||||
pub use rules::pycodestyle::rules::IOError;
|
||||
pub use violation::{AutofixKind, Availability as AutofixAvailability};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(not(target_family = "wasm"))] {
|
||||
pub mod packaging;
|
||||
|
||||
mod lib_native;
|
||||
pub use lib_native::check;
|
||||
} else {
|
||||
if #[cfg(target_family = "wasm")] {
|
||||
mod lib_wasm;
|
||||
pub use lib_wasm::check;
|
||||
} else {
|
||||
pub mod packaging;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use path_absolutize::path_dedot;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
use crate::linter::check_path;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::resolver::Relativity;
|
||||
use crate::rustpython_helpers::tokenize;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::{flags, pyproject, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
use crate::{directives, packaging, resolver};
|
||||
|
||||
/// Load the relevant `Settings` for a given `Path`.
|
||||
fn resolve(path: &Path) -> Result<Settings> {
|
||||
if let Some(pyproject) = pyproject::find_settings_toml(path)? {
|
||||
// First priority: `pyproject.toml` in the current `Path`.
|
||||
Ok(resolver::resolve_settings(&pyproject, &Relativity::Parent)?.lib)
|
||||
} else if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
// Second priority: user-specific `pyproject.toml`.
|
||||
Ok(resolver::resolve_settings(&pyproject, &Relativity::Cwd)?.lib)
|
||||
} else {
|
||||
// Fallback: default settings.
|
||||
Settings::from_configuration(Configuration::default(), &path_dedot::CWD)
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Ruff over Python source code directly.
|
||||
pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Diagnostic>> {
|
||||
// Load the relevant `Settings` for the given `Path`.
|
||||
let settings = resolve(path)?;
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = tokenize(contents);
|
||||
|
||||
// Map row and column locations to byte slices (lazily).
|
||||
let locator = Locator::new(contents);
|
||||
|
||||
// Detect the current code style (lazily).
|
||||
let stylist = Stylist::from_contents(contents, &locator);
|
||||
|
||||
// Extra indices from the code.
|
||||
let indexer: Indexer = tokens.as_slice().into();
|
||||
|
||||
// Extract the `# noqa` and `# isort: skip` directives from the source.
|
||||
let directives =
|
||||
directives::extract_directives(&tokens, directives::Flags::from_settings(&settings));
|
||||
|
||||
// Generate diagnostics.
|
||||
let result = check_path(
|
||||
path,
|
||||
packaging::detect_package_root(path, &settings.namespace_packages),
|
||||
contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
&directives,
|
||||
&settings,
|
||||
autofix.into(),
|
||||
flags::Noqa::Enabled,
|
||||
);
|
||||
|
||||
Ok(result.data)
|
||||
}
|
||||
@@ -66,7 +66,7 @@ impl Serialize for SerializeRuleAsCode {
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.0.code())
|
||||
serializer.serialize_str(&self.0.noqa_code().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
show_fixes: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
task_tags: None,
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::path::Path;
|
||||
use anyhow::{anyhow, Result};
|
||||
use colored::Colorize;
|
||||
use log::error;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::error::ParseError;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
@@ -18,7 +19,7 @@ use crate::checkers::tokens::check_tokens;
|
||||
use crate::directives::Directives;
|
||||
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
|
||||
use crate::message::{Message, Source};
|
||||
use crate::noqa::add_noqa;
|
||||
use crate::noqa::{add_noqa, rule_is_ignored};
|
||||
use crate::registry::{Diagnostic, LintSource, Rule};
|
||||
use crate::rules::pycodestyle;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -46,6 +47,8 @@ impl<T> LinterResult<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub type FixTable = FxHashMap<&'static Rule, usize>;
|
||||
|
||||
/// Generate `Diagnostic`s from the source code contents at the
|
||||
/// given `Path`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -149,7 +152,17 @@ pub fn check_path(
|
||||
if settings.rules.enabled(&Rule::SyntaxError) {
|
||||
pycodestyle::rules::syntax_error(&mut diagnostics, &parse_error);
|
||||
}
|
||||
error = Some(parse_error);
|
||||
|
||||
// If the syntax error is ignored, suppress it (regardless of whether
|
||||
// `Rule::SyntaxError` is enabled).
|
||||
if !rule_is_ignored(
|
||||
&Rule::SyntaxError,
|
||||
parse_error.location.row(),
|
||||
&directives.noqa_line_for,
|
||||
locator,
|
||||
) {
|
||||
error = Some(parse_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +211,7 @@ pub fn check_path(
|
||||
indexer.commented_lines(),
|
||||
&directives.noqa_line_for,
|
||||
settings,
|
||||
autofix,
|
||||
error.as_ref().map_or(autofix, |_| flags::Autofix::Disabled),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,8 +220,8 @@ pub fn check_path(
|
||||
|
||||
const MAX_ITERATIONS: usize = 100;
|
||||
|
||||
/// Add any missing `#noqa` pragmas to the source code at the given `Path`.
|
||||
pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
/// Add any missing `# noqa` pragmas to the source code at the given `Path`.
|
||||
pub fn add_noqa_to_path(path: &Path, package: Option<&Path>, settings: &Settings) -> Result<usize> {
|
||||
// Read the file from disk.
|
||||
let contents = fs::read_file(path)?;
|
||||
|
||||
@@ -234,7 +247,7 @@ pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
error,
|
||||
} = check_path(
|
||||
path,
|
||||
None,
|
||||
package,
|
||||
&contents,
|
||||
tokens,
|
||||
&locator,
|
||||
@@ -331,11 +344,11 @@ pub fn lint_fix<'a>(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
settings: &Settings,
|
||||
) -> Result<(LinterResult<Vec<Message>>, Cow<'a, str>, usize)> {
|
||||
) -> Result<(LinterResult<Vec<Message>>, Cow<'a, str>, FixTable)> {
|
||||
let mut transformed = Cow::Borrowed(contents);
|
||||
|
||||
// Track the number of fixed errors across iterations.
|
||||
let mut fixed = 0;
|
||||
let mut fixed = FxHashMap::default();
|
||||
|
||||
// As an escape hatch, bail after 100 iterations.
|
||||
let mut iterations = 0;
|
||||
@@ -409,7 +422,9 @@ This indicates a bug in `{}`. If you could open an issue at:
|
||||
if let Some((fixed_contents, applied)) = fix_file(&result.data, &locator) {
|
||||
if iterations < MAX_ITERATIONS {
|
||||
// Count the number of fixed errors.
|
||||
fixed += applied;
|
||||
for (rule, count) in applied {
|
||||
*fixed.entry(rule).or_default() += count;
|
||||
}
|
||||
|
||||
// Store the fixed contents.
|
||||
transformed = Cow::Owned(fixed_contents);
|
||||
|
||||
@@ -71,9 +71,9 @@ impl Source {
|
||||
} else {
|
||||
Location::new(diagnostic.end_location.row() + 1, 0)
|
||||
};
|
||||
let source = locator.slice_source_code_range(&Range::new(location, end_location));
|
||||
let source = locator.slice(&Range::new(location, end_location));
|
||||
let num_chars_in_range = locator
|
||||
.slice_source_code_range(&Range::new(diagnostic.location, diagnostic.end_location))
|
||||
.slice(&Range::new(diagnostic.location, diagnostic.end_location))
|
||||
.chars()
|
||||
.count();
|
||||
Source {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::fmt::{Display, Write};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -7,10 +8,12 @@ use nohash_hasher::IntMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::source_code::LineEnding;
|
||||
use crate::source_code::{LineEnding, Locator};
|
||||
|
||||
static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
@@ -70,12 +73,31 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
/// Returns `true` if the string list of `codes` includes `code` (or an alias
|
||||
/// thereof).
|
||||
pub fn includes(needle: &Rule, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.code();
|
||||
let needle = needle.noqa_code();
|
||||
haystack
|
||||
.iter()
|
||||
.any(|candidate| needle == get_redirect_target(candidate).unwrap_or(candidate))
|
||||
}
|
||||
|
||||
/// Returns `true` if the given [`Rule`] is ignored at the specified `lineno`.
|
||||
pub fn rule_is_ignored(
|
||||
code: &Rule,
|
||||
lineno: usize,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
locator: &Locator,
|
||||
) -> bool {
|
||||
let noqa_lineno = noqa_line_for.get(&lineno).unwrap_or(&lineno);
|
||||
let line = locator.slice(&Range::new(
|
||||
Location::new(*noqa_lineno, 0),
|
||||
Location::new(noqa_lineno + 1, 0),
|
||||
));
|
||||
match extract_noqa_directive(line) {
|
||||
Directive::None => false,
|
||||
Directive::All(..) => true,
|
||||
Directive::Codes(.., codes) => includes(code, &codes),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_noqa(
|
||||
path: &Path,
|
||||
diagnostics: &[Diagnostic],
|
||||
@@ -184,9 +206,7 @@ fn add_noqa_inner(
|
||||
output.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = rules.iter().map(|r| r.code()).collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
push_codes(&mut output, rules.iter().map(|r| r.noqa_code()));
|
||||
output.push_str(line_ending);
|
||||
count += 1;
|
||||
}
|
||||
@@ -207,14 +227,14 @@ fn add_noqa_inner(
|
||||
formatted.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = rules
|
||||
.iter()
|
||||
.map(|r| r.code())
|
||||
.chain(existing.into_iter())
|
||||
.sorted_unstable()
|
||||
.collect();
|
||||
let suffix = codes.join(", ");
|
||||
formatted.push_str(&suffix);
|
||||
push_codes(
|
||||
&mut formatted,
|
||||
rules
|
||||
.iter()
|
||||
.map(|r| r.noqa_code().to_string())
|
||||
.chain(existing.into_iter().map(ToString::to_string))
|
||||
.sorted_unstable(),
|
||||
);
|
||||
|
||||
output.push_str(&formatted);
|
||||
output.push_str(line_ending);
|
||||
@@ -232,6 +252,17 @@ fn add_noqa_inner(
|
||||
(count, output)
|
||||
}
|
||||
|
||||
fn push_codes<I: Display>(str: &mut String, codes: impl Iterator<Item = I>) {
|
||||
let mut first = true;
|
||||
for code in codes {
|
||||
if !first {
|
||||
str.push_str(", ");
|
||||
}
|
||||
let _ = write!(str, "{}", code);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nohash_hasher::IntMap;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ use std::sync::RwLock;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use ignore::{DirEntry, WalkBuilder, WalkState};
|
||||
use itertools::Itertools;
|
||||
use log::debug;
|
||||
use path_absolutize::path_dedot;
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -217,7 +218,7 @@ pub fn python_files_in_path(
|
||||
processor: impl ConfigProcessor,
|
||||
) -> Result<(Vec<Result<DirEntry, ignore::Error>>, Resolver)> {
|
||||
// Normalize every path (e.g., convert from relative to absolute).
|
||||
let mut paths: Vec<PathBuf> = paths.iter().map(fs::normalize_path).collect();
|
||||
let mut paths: Vec<PathBuf> = paths.iter().map(fs::normalize_path).unique().collect();
|
||||
|
||||
// Search for `pyproject.toml` files in all parent directories.
|
||||
let mut resolver = Resolver::default();
|
||||
|
||||
@@ -15,6 +15,11 @@ pub(crate) fn get_redirect(code: &str) -> Option<(&'static str, &'static str)> {
|
||||
|
||||
static REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
HashMap::from_iter([
|
||||
("C", "C4"),
|
||||
("C9", "C90"),
|
||||
("T", "T10"),
|
||||
("T1", "T10"),
|
||||
("T2", "T20"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("R", "RET"),
|
||||
("R5", "RET5"),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use itertools::Itertools;
|
||||
use schemars::_serde_json::Value;
|
||||
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
use schemars::JsonSchema;
|
||||
@@ -8,19 +9,27 @@ use serde::{Deserialize, Serialize};
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
use crate::registry::{Rule, RuleCodePrefix, RuleIter};
|
||||
use crate::codes::RuleCodePrefix;
|
||||
use crate::registry::{Linter, Rule, RuleIter, RuleNamespace};
|
||||
use crate::rule_redirects::get_redirect;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum RuleSelector {
|
||||
/// All rules
|
||||
All,
|
||||
Linter(Linter),
|
||||
Prefix {
|
||||
prefix: RuleCodePrefix,
|
||||
redirected_from: Option<&'static str>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Linter> for RuleSelector {
|
||||
fn from(linter: Linter) -> Self {
|
||||
Self::Linter(linter)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RuleSelector {
|
||||
type Err = ParseError;
|
||||
|
||||
@@ -32,9 +41,17 @@ impl FromStr for RuleSelector {
|
||||
Some((from, target)) => (target, Some(from)),
|
||||
None => (s, None),
|
||||
};
|
||||
|
||||
let (linter, code) =
|
||||
Linter::parse_code(s).ok_or_else(|| ParseError::Unknown(s.to_string()))?;
|
||||
|
||||
if code.is_empty() {
|
||||
return Ok(Self::Linter(linter));
|
||||
}
|
||||
|
||||
Ok(Self::Prefix {
|
||||
prefix: RuleCodePrefix::from_str(s)
|
||||
.map_err(|_| ParseError::Unknown(s.to_string()))?,
|
||||
prefix: RuleCodePrefix::parse(&linter, code)
|
||||
.map_err(|_| ParseError::Unknown(code.to_string()))?,
|
||||
redirected_from,
|
||||
})
|
||||
}
|
||||
@@ -50,10 +67,13 @@ pub enum ParseError {
|
||||
}
|
||||
|
||||
impl RuleSelector {
|
||||
pub fn short_code(&self) -> &'static str {
|
||||
pub fn prefix_and_code(&self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
RuleSelector::All => "ALL",
|
||||
RuleSelector::Prefix { prefix, .. } => prefix.into(),
|
||||
RuleSelector::All => ("", "ALL"),
|
||||
RuleSelector::Prefix { prefix, .. } => {
|
||||
(prefix.linter().common_prefix(), prefix.short_code())
|
||||
}
|
||||
RuleSelector::Linter(l) => (l.common_prefix(), ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +83,8 @@ impl Serialize for RuleSelector {
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.short_code())
|
||||
let (prefix, code) = self.prefix_and_code();
|
||||
serializer.serialize_str(&format!("{prefix}{code}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,14 +138,15 @@ impl IntoIterator for &RuleSelector {
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
RuleSelector::All => RuleSelectorIter::All(Rule::iter()),
|
||||
RuleSelector::Prefix { prefix, .. } => RuleSelectorIter::Prefix(prefix.into_iter()),
|
||||
RuleSelector::Linter(linter) => RuleSelectorIter::Vec(linter.into_iter()),
|
||||
RuleSelector::Prefix { prefix, .. } => RuleSelectorIter::Vec(prefix.into_iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RuleSelectorIter {
|
||||
All(RuleIter),
|
||||
Prefix(std::vec::IntoIter<Rule>),
|
||||
Vec(std::vec::IntoIter<Rule>),
|
||||
}
|
||||
|
||||
impl Iterator for RuleSelectorIter {
|
||||
@@ -133,7 +155,7 @@ impl Iterator for RuleSelectorIter {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
RuleSelectorIter::All(iter) => iter.next(),
|
||||
RuleSelectorIter::Prefix(iter) => iter.next(),
|
||||
RuleSelectorIter::Vec(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,7 +182,19 @@ impl JsonSchema for RuleSelector {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
enum_values: Some(
|
||||
std::iter::once("ALL".to_string())
|
||||
.chain(RuleCodePrefix::iter().map(|s| s.as_ref().to_string()))
|
||||
.chain(
|
||||
RuleCodePrefix::iter()
|
||||
.map(|p| {
|
||||
let prefix = p.linter().common_prefix();
|
||||
let code = p.short_code();
|
||||
format!("{prefix}{code}")
|
||||
})
|
||||
.chain(Linter::iter().filter_map(|l| {
|
||||
let prefix = l.common_prefix();
|
||||
(!prefix.is_empty()).then(|| prefix.to_string())
|
||||
}))
|
||||
.sorted(),
|
||||
)
|
||||
.map(Value::String)
|
||||
.collect(),
|
||||
),
|
||||
@@ -173,7 +207,18 @@ impl RuleSelector {
|
||||
pub(crate) fn specificity(&self) -> Specificity {
|
||||
match self {
|
||||
RuleSelector::All => Specificity::All,
|
||||
RuleSelector::Prefix { prefix, .. } => prefix.specificity(),
|
||||
RuleSelector::Linter(..) => Specificity::Linter,
|
||||
RuleSelector::Prefix { prefix, .. } => {
|
||||
let prefix: &'static str = prefix.short_code();
|
||||
match prefix.len() {
|
||||
1 => Specificity::Code1Char,
|
||||
2 => Specificity::Code2Chars,
|
||||
3 => Specificity::Code3Chars,
|
||||
4 => Specificity::Code4Chars,
|
||||
5 => Specificity::Code5Chars,
|
||||
_ => panic!("RuleSelector::specificity doesn't yet support codes with so many characters"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,3 +233,76 @@ pub(crate) enum Specificity {
|
||||
Code4Chars,
|
||||
Code5Chars,
|
||||
}
|
||||
|
||||
mod clap_completion {
|
||||
use clap::builder::{PossibleValue, TypedValueParser, ValueParserFactory};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{
|
||||
codes::RuleCodePrefix,
|
||||
registry::{Linter, RuleNamespace},
|
||||
RuleSelector,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RuleSelectorParser;
|
||||
|
||||
impl ValueParserFactory for RuleSelector {
|
||||
type Parser = RuleSelectorParser;
|
||||
|
||||
fn value_parser() -> Self::Parser {
|
||||
RuleSelectorParser
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedValueParser for RuleSelectorParser {
|
||||
type Value = RuleSelector;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
_cmd: &clap::Command,
|
||||
_arg: Option<&clap::Arg>,
|
||||
value: &std::ffi::OsStr,
|
||||
) -> Result<Self::Value, clap::Error> {
|
||||
let value = value
|
||||
.to_str()
|
||||
.ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
|
||||
|
||||
value
|
||||
.parse()
|
||||
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::InvalidValue, e))
|
||||
}
|
||||
|
||||
fn possible_values(
|
||||
&self,
|
||||
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
|
||||
Some(Box::new(
|
||||
std::iter::once(PossibleValue::new("ALL").help("all rules")).chain(
|
||||
Linter::iter()
|
||||
.filter_map(|l| {
|
||||
let prefix = l.common_prefix();
|
||||
(!prefix.is_empty()).then(|| PossibleValue::new(prefix).help(l.name()))
|
||||
})
|
||||
.chain(RuleCodePrefix::iter().map(|p| {
|
||||
let prefix = p.linter().common_prefix();
|
||||
let code = p.short_code();
|
||||
|
||||
let mut rules_iter = p.into_iter();
|
||||
let rule1 = rules_iter.next();
|
||||
let rule2 = rules_iter.next();
|
||||
|
||||
let value = PossibleValue::new(format!("{prefix}{code}"));
|
||||
|
||||
if rule2.is_none() {
|
||||
let rule1 = rule1.unwrap();
|
||||
let name: &'static str = rule1.into();
|
||||
value.help(name)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
})),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ mod tests {
|
||||
|
||||
#[test_case(Rule::CommentedOutCode, Path::new("ERA001.py"); "ERA001")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("eradicate").join(path).as_path(),
|
||||
&settings::Settings::for_rule(rule_code),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user