Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
add7fefeb5 | ||
|
|
ec24947865 | ||
|
|
8038d32649 | ||
|
|
0362cc1098 | ||
|
|
860e3110c0 | ||
|
|
0fa8c578cb | ||
|
|
861df12269 | ||
|
|
071e3fd196 | ||
|
|
dd79ec293a | ||
|
|
5d331e43bf | ||
|
|
ff3563b8ce | ||
|
|
caada2f8bb | ||
|
|
0b4cc5ac12 | ||
|
|
8c70247188 | ||
|
|
eaac3cae5e | ||
|
|
fd56414b2f | ||
|
|
9731f96fb4 | ||
|
|
1a0191f1ac | ||
|
|
249cf73d4e | ||
|
|
eda2be6350 | ||
|
|
57a68f7c7d | ||
|
|
a19dd9237b | ||
|
|
4f067d806e | ||
|
|
dd15c69181 | ||
|
|
b692921160 | ||
|
|
b3e8b1b787 | ||
|
|
0b34ca7107 | ||
|
|
df44c5124e | ||
|
|
0e27f78b3f | ||
|
|
cd8ad1df08 | ||
|
|
d1aaf16e40 | ||
|
|
7320058ce2 | ||
|
|
3a8b367b1c | ||
|
|
221b87332c | ||
|
|
8149c8cbc4 | ||
|
|
2c415016a6 | ||
|
|
bb85119ba8 | ||
|
|
94551a203e | ||
|
|
64c4e4c6c7 | ||
|
|
84e4b7c96f | ||
|
|
ca26f664ec | ||
|
|
779b232db9 | ||
|
|
a316b26b49 |
94
.github/workflows/ci.yaml
vendored
94
.github/workflows/ci.yaml
vendored
@@ -22,16 +22,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo build --all
|
||||
- run: ./target/debug/ruff_dev generate-all
|
||||
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
||||
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo +nightly dev generate-all'."
|
||||
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo dev generate-all'."
|
||||
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo dev generate-all'."
|
||||
- run: git diff --exit-code -- README.md ruff.schema.json
|
||||
|
||||
cargo-fmt:
|
||||
@@ -39,12 +36,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
components: rustfmt
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup component add rustfmt
|
||||
- run: cargo fmt --all --check
|
||||
|
||||
cargo_clippy:
|
||||
@@ -52,13 +45,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
components: clippy
|
||||
target: wasm32-unknown-unknown
|
||||
- name: "Install Rust toolchain"
|
||||
run: |
|
||||
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 -W clippy::pedantic
|
||||
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings -W clippy::pedantic
|
||||
@@ -71,20 +61,17 @@ jobs:
|
||||
name: "cargo test | ${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.65.0
|
||||
override: true
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo install cargo-insta
|
||||
- run: pip install black[d]==22.12.0
|
||||
- name: Run tests (Ubuntu)
|
||||
- name: "Run tests (Ubuntu)"
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
cargo insta test --all --delete-unreferenced-snapshots
|
||||
git diff --exit-code
|
||||
- name: Run tests (Windows)
|
||||
- name: "Run tests (Windows)"
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -102,10 +89,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
|
||||
- run: cargo check
|
||||
@@ -114,61 +99,26 @@ jobs:
|
||||
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
|
||||
- run: cargo check
|
||||
|
||||
# TODO(charlie): Re-enable the `wasm-pack` tests.
|
||||
# See: https://github.com/charliermarsh/ruff/issues/1425
|
||||
# wasm-pack-test:
|
||||
# name: "wasm-pack test"
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# WASM_BINDGEN_TEST_TIMEOUT: 60
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: actions-rs/toolchain@v1
|
||||
# with:
|
||||
# profile: minimal
|
||||
# toolchain: nightly-2022-11-01
|
||||
# override: true
|
||||
# - uses: actions/cache@v3
|
||||
# env:
|
||||
# cache-name: cache-cargo
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.cargo/registry
|
||||
# ~/.cargo/git
|
||||
# key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
# ${{ runner.os }}-build-
|
||||
# ${{ runner.os }}-
|
||||
# - uses: jetli/wasm-pack-action@v0.4.0
|
||||
# - uses: jetli/wasm-bindgen-action@v0.2.0
|
||||
# - run: wasm-pack test --node
|
||||
|
||||
maturin-build:
|
||||
name: "maturin build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.65.0
|
||||
override: true
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- run: pip install maturin
|
||||
- run: maturin build -b bin
|
||||
- run: python scripts/transform_readme.py --target pypi
|
||||
|
||||
typos:
|
||||
name: Spell Check with Typos
|
||||
name: "spell check"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check spelling of file.txt
|
||||
uses: crate-ci/typos@master
|
||||
- uses: actions/checkout@v3
|
||||
- uses: crate-ci/typos@master
|
||||
with:
|
||||
files: .
|
||||
|
||||
35
.github/workflows/docs.yaml
vendored
Normal file
35
.github/workflows/docs.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: mkdocs
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- README.md
|
||||
- mkdocs.template.yml
|
||||
- .github/workflows/docs.yaml
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
mkdocs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
pip install "mkdocs~=1.4.2" "mkdocs-material~=9.0.6"
|
||||
- name: "Copy README File"
|
||||
run: |
|
||||
python scripts/transform_readme.py --target mkdocs
|
||||
python scripts/generate_mkdocs.py
|
||||
mkdocs build --strict
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
command: pages publish site --project-name=ruff-docs --branch ${GITHUB_HEAD_REF} --commit-hash ${GITHUB_SHA}
|
||||
70
.github/workflows/flake8-to-ruff.yaml
vendored
70
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -24,21 +24,17 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build wheels - x86_64
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: Install built wheel - x86_64
|
||||
- name: "Install built wheel - x86_64"
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -51,20 +47,16 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build wheels - universal2
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels - universal2"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: Install built wheel - universal2
|
||||
- name: "Install built wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*universal2.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -81,22 +73,18 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.target }}
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build wheels
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: Install built wheel
|
||||
- name: "Install built wheel"
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -113,17 +101,17 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Build wheels
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: Install built wheel
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -139,7 +127,7 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Build wheels
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
@@ -158,7 +146,7 @@ jobs:
|
||||
pip3 install -U pip
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -177,13 +165,13 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Build wheels
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: Install built wheel
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
@@ -192,7 +180,7 @@ jobs:
|
||||
run: |
|
||||
apk add py3-pip
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -212,7 +200,7 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Build wheels
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
@@ -228,7 +216,7 @@ jobs:
|
||||
apk add py3-pip
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -252,17 +240,17 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: pypy${{ matrix.python-version }}
|
||||
- name: Build wheels
|
||||
- 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
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -285,7 +273,7 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
- uses: actions/setup-python@v4
|
||||
- name: Publish to PyPi
|
||||
- name: "Publish to PyPi"
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.FLAKE8_TO_RUFF_TOKEN }}
|
||||
|
||||
8
.github/workflows/playground.yaml
vendored
8
.github/workflows/playground.yaml
vendored
@@ -18,12 +18,8 @@ jobs:
|
||||
CF_API_TOKEN_EXISTS: ${{ secrets.CF_API_TOKEN != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
override: true
|
||||
target: wasm32-unknown-unknown
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
82
.github/workflows/ruff.yaml
vendored
82
.github/workflows/ruff.yaml
vendored
@@ -26,21 +26,17 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build wheels - x86_64
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist
|
||||
- name: Install built wheel - x86_64
|
||||
- name: "Install built wheel - x86_64"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -53,20 +49,16 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build wheels - universal2
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - universal2"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist
|
||||
- name: Install built wheel - universal2
|
||||
- name: "Install built wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -83,22 +75,18 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.target }}
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build wheels
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist
|
||||
- name: Install built wheel
|
||||
- name: "Install built wheel"
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -115,17 +103,19 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Build wheels
|
||||
- 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
|
||||
- name: Install built wheel
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -141,7 +131,9 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Build wheels
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
@@ -160,7 +152,7 @@ jobs:
|
||||
pip3 install -U pip
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -179,13 +171,15 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: Build wheels
|
||||
- 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: musllinux_1_2
|
||||
args: --release --out dist
|
||||
- name: Install built wheel
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
@@ -194,7 +188,7 @@ jobs:
|
||||
run: |
|
||||
apk add py3-pip
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -214,7 +208,9 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Build wheels
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
@@ -230,7 +226,7 @@ jobs:
|
||||
apk add py3-pip
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -254,17 +250,19 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: pypy${{ matrix.python-version }}
|
||||
- name: Build wheels
|
||||
- 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
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: Upload wheels
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
@@ -288,13 +286,13 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
- uses: actions/setup-python@v4
|
||||
- name: Publish to PyPi
|
||||
- name: "Publish to PyPi"
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.RUFF_TOKEN }}
|
||||
run: |
|
||||
pip install --upgrade twine
|
||||
twine upload --skip-existing *
|
||||
- name: Update pre-commit mirror
|
||||
- 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"}'
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
||||
# Local cache
|
||||
.ruff_cache
|
||||
resources/test/cpython
|
||||
docs/
|
||||
mkdocs.yml
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.236
|
||||
rev: v0.0.237
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
@@ -15,6 +15,17 @@ repos:
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo +nightly fmt --
|
||||
entry: cargo fmt --
|
||||
language: rust
|
||||
types: [rust]
|
||||
- id: clippy
|
||||
name: clippy
|
||||
entry: cargo clippy --workspace --all-targets --all-features
|
||||
language: rust
|
||||
pass_filenames: false
|
||||
- id: dev-generate-all
|
||||
name: dev-generate-all
|
||||
entry: cargo dev generate-all
|
||||
language: rust
|
||||
pass_filenames: false
|
||||
exclude: target
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.237
|
||||
|
||||
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/charliermarsh/ruff/pull/2190))
|
||||
|
||||
`--explain`, `--clean`, and `--generate-shell-completion` are now implemented as subcommands:
|
||||
|
||||
ruff . # Still works! And will always work.
|
||||
ruff check . # New! Also works.
|
||||
|
||||
ruff --explain E402 # Still works.
|
||||
ruff rule E402 # New! Also works. (And preferred.)
|
||||
|
||||
# Oops! The command has to come first.
|
||||
ruff --format json --explain E402 # No longer works.
|
||||
ruff --explain E402 --format json # Still works!
|
||||
ruff rule E402 --format json # Works! (And preferred.)
|
||||
|
||||
This change is largely backwards compatible -- most users should experience
|
||||
no change in behavior. However, please note the following exceptions:
|
||||
|
||||
* Subcommands will now fail when invoked with unsupported arguments, instead
|
||||
of silently ignoring them. For example, the following will now fail:
|
||||
|
||||
ruff --clean --respect-gitignore
|
||||
|
||||
(the `clean` command doesn't support `--respect-gitignore`.)
|
||||
|
||||
* The semantics of `ruff <arg>` have changed slightly when `<arg>` is a valid subcommand.
|
||||
For example, prior to this release, running `ruff rule` would run `ruff` over a file or
|
||||
directory called `rule`. Now, `ruff rule` would invoke the `rule` subcommand. This should
|
||||
only impact projects with files or directories named `rule`, `check`, `explain`, `clean`,
|
||||
or `generate-shell-completion`.
|
||||
|
||||
* Scripts that invoke ruff should supply `--` before any positional arguments.
|
||||
(The semantics of `ruff -- <arg>` have not changed.)
|
||||
|
||||
* `--explain` previously treated `--format grouped` as a synonym for `--format text`.
|
||||
This is no longer supported; instead, use `--format text`.
|
||||
|
||||
## 0.0.226
|
||||
|
||||
### `misplaced-comparison-constant` (`PLC2201`) was deprecated in favor of `SIM300` ([#1980](https://github.com/charliermarsh/ruff/pull/1980))
|
||||
|
||||
@@ -39,20 +39,24 @@ cargo run resources/test/fixtures --no-cache
|
||||
```
|
||||
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted,
|
||||
and that it passes both the lint and test validation checks.
|
||||
|
||||
For rustfmt and Clippy, we use [nightly Rust][nightly], as it is stricter than stable Rust.
|
||||
(However, tests and builds use stable Rust.)
|
||||
and that it passes both the lint and test validation checks:
|
||||
|
||||
```shell
|
||||
cargo +nightly fmt --all # Auto-formatting...
|
||||
cargo +nightly clippy --fix --workspace --all-targets --all-features # Linting...
|
||||
cargo fmt --all # Auto-formatting...
|
||||
cargo clippy --fix --workspace --all-targets --all-features # Linting...
|
||||
cargo test --all # Testing...
|
||||
```
|
||||
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
will save you time and expedite the merge process.
|
||||
|
||||
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
|
||||
assist with formatting and linting. The following command will run the `pre-commit` hooks:
|
||||
|
||||
```shell
|
||||
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.
|
||||
|
||||
@@ -79,7 +83,7 @@ To trigger the violation, you'll likely want to augment the logic in `src/checke
|
||||
defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
|
||||
collecting diagnostics as it goes.
|
||||
|
||||
If you need to inspect the AST, you can run `cargo +nightly dev print-ast` with a Python file. Grep
|
||||
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
|
||||
for the `Check::new` invocations to understand how other, similar rules are implemented.
|
||||
|
||||
To add a test fixture, create a file under `resources/test/fixtures/[linter]`, named to match
|
||||
@@ -87,7 +91,7 @@ the code you defined earlier (e.g., `resources/test/fixtures/pycodestyle/E402.py
|
||||
contain a variety of violations and non-violations designed to evaluate and demonstrate the behavior
|
||||
of your lint rule.
|
||||
|
||||
Run `cargo +nightly dev generate-all` to generate the code for your new fixture. Then run Ruff
|
||||
Run `cargo dev generate-all` to generate the code for your new fixture. Then run Ruff
|
||||
locally with (e.g.) `cargo run resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
|
||||
|
||||
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
|
||||
@@ -95,7 +99,7 @@ Once you're satisfied with the output, codify the behavior as a snapshot test by
|
||||
Your test will fail, but you'll be prompted to follow-up with `cargo insta review`. Accept the
|
||||
generated snapshot, then commit the snapshot file alongside the rest of your changes.
|
||||
|
||||
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
|
||||
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
|
||||
|
||||
### Example: Adding a new configuration option
|
||||
|
||||
@@ -121,7 +125,7 @@ You may also want to add the new configuration option to the `flake8-to-ruff` to
|
||||
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
|
||||
lives in `flake8_to_ruff/src/converter.rs`.
|
||||
|
||||
Finally, regenerate the documentation and generated code with `cargo +nightly dev generate-all`.
|
||||
Finally, regenerate the documentation and generated code with `cargo dev generate-all`.
|
||||
|
||||
## Release process
|
||||
|
||||
@@ -131,5 +135,3 @@ them to [PyPI](https://pypi.org/project/ruff/).
|
||||
|
||||
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
|
||||
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
|
||||
|
||||
[nightly]: https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust
|
||||
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -750,7 +750,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -1922,7 +1922,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
@@ -1977,7 +1977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2014,7 +2014,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -2035,7 +2035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -46,7 +46,7 @@ num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff_macros = { version = "0.0.236", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.237", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
|
||||
251
README.md
251
README.md
@@ -1,3 +1,5 @@
|
||||
<!-- Begin section: Overview -->
|
||||
|
||||
# Ruff
|
||||
|
||||
[](https://github.com/charliermarsh/ruff)
|
||||
@@ -106,6 +108,8 @@ developer of [Zulip](https://github.com/zulip/zulip):
|
||||
|
||||
> This is just ridiculously fast... `ruff` is amazing.
|
||||
|
||||
<!-- End section: Overview -->
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
@@ -161,6 +165,8 @@ developer of [Zulip](https://github.com/zulip/zulip):
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
<!-- Begin section: Installation and Usage -->
|
||||
|
||||
### Installation
|
||||
|
||||
Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
@@ -216,13 +222,17 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.236'
|
||||
rev: 'v0.0.237'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
|
||||
<!-- 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
|
||||
options, see the [API reference](#reference).
|
||||
|
||||
@@ -315,7 +325,10 @@ prefix, followed by three digits (e.g., `F401`). The prefix indicates that "sour
|
||||
rules is determined by the `select` and `ignore` options, which support both the full code (e.g.,
|
||||
`F401`) and the prefix (e.g., `F`).
|
||||
|
||||
As a special-case, Ruff also supports the `ALL` code, which enables all rules.
|
||||
As a special-case, Ruff also supports the `ALL` code, which enables all rules. Note that some of the
|
||||
`pydocstyle` rules conflict (e.g., `D203` and `D211`) as they represent alternative docstring
|
||||
formats. Enabling `ALL` without further configuration may result in suboptimal behavior, especially
|
||||
for the `pydocstyle` plugin.
|
||||
|
||||
If you're wondering how to configure Ruff, here are some **recommended guidelines**:
|
||||
|
||||
@@ -363,77 +376,24 @@ See `ruff --help` for more:
|
||||
```
|
||||
Ruff: An extremely fast Python linter.
|
||||
|
||||
Usage: ruff [OPTIONS] [FILES]...
|
||||
Usage: ruff [OPTIONS] <COMMAND>
|
||||
|
||||
Arguments:
|
||||
[FILES]...
|
||||
Commands:
|
||||
check Run Ruff on the given files or directories (default)
|
||||
rule Explain a rule
|
||||
clean Clear any caches in the current directory and any subdirectories
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
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
|
||||
--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
|
||||
-V, --version Print version
|
||||
|
||||
Rule selection:
|
||||
--select <RULE_CODE>
|
||||
Comma-separated list of rule codes to enable (or ALL, to enable all rules)
|
||||
--ignore <RULE_CODE>
|
||||
Comma-separated list of rule codes to disable
|
||||
--extend-select <RULE_CODE>
|
||||
Like --select, but adds additional rule codes on top of the selected ones
|
||||
--extend-ignore <RULE_CODE>
|
||||
Like --ignore, but adds additional rule codes on top of the ignored ones
|
||||
--per-file-ignores <PER_FILE_IGNORES>
|
||||
List of mappings from file pattern to code to exclude
|
||||
--fixable <RULE_CODE>
|
||||
List of rule codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--unfixable <RULE_CODE>
|
||||
List of rule codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
|
||||
File selection:
|
||||
--exclude <FILE_PATTERN> List of paths, used to omit files and/or directories from analysis
|
||||
--extend-exclude <FILE_PATTERN> Like --exclude, but adds additional files and directories on top of those already excluded
|
||||
--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
|
||||
--isolated
|
||||
Ignore all configuration files
|
||||
--cache-dir <CACHE_DIR>
|
||||
Path to the cache directory [env: RUFF_CACHE_DIR=]
|
||||
--stdin-filename <STDIN_FILENAME>
|
||||
The name of the file when passing it through stdin
|
||||
-e, --exit-zero
|
||||
Exit with status code "0", even upon detecting lint violations
|
||||
--update-check
|
||||
Enable or disable automatic update checks
|
||||
|
||||
Subcommands:
|
||||
--explain <EXPLAIN> Explain a rule
|
||||
--clean Clear any caches in the current directory or any subdirectories
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
|
||||
Log levels:
|
||||
-v, --verbose Enable verbose logging
|
||||
-q, --quiet Print lint violations, but nothing else
|
||||
-s, --silent Disable all logging (but still exit with status code "1" upon detecting lint violations)
|
||||
|
||||
For help with a specific command, see: `ruff help <command>`.
|
||||
```
|
||||
<!-- End auto-generated cli help. -->
|
||||
|
||||
@@ -454,10 +414,9 @@ There are a few exceptions to these rules:
|
||||
resolved relative to the _current working directory_.
|
||||
3. If no `pyproject.toml` file is found in the filesystem hierarchy, Ruff will fall back to using
|
||||
a default configuration. If a user-specific configuration file exists
|
||||
at `${config_dir}/ruff/pyproject.toml`,
|
||||
that file will be used instead of the default configuration, with `${config_dir}` being
|
||||
determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) crate, and all
|
||||
relative paths being again resolved relative to the _current working directory_.
|
||||
at `${config_dir}/ruff/pyproject.toml`, that file will be used instead of the default
|
||||
configuration, with `${config_dir}` being determined via the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html)
|
||||
crate, and all relative paths being again resolved relative to the _current working directory_.
|
||||
4. Any `pyproject.toml`-supported settings that are provided on the command-line (e.g., via
|
||||
`--select`) will override the settings in _every_ resolved configuration file.
|
||||
|
||||
@@ -552,8 +511,12 @@ Third, Ruff can _automatically add_ `noqa` directives to all failing lines. This
|
||||
migrating a new codebase to Ruff. You can run `ruff /path/to/file.py --add-noqa` to automatically
|
||||
add `noqa` directives to all failing lines, with the appropriate rule codes.
|
||||
|
||||
<!-- End section: Configuration -->
|
||||
|
||||
## Supported Rules
|
||||
|
||||
<!-- Begin section: Rules -->
|
||||
|
||||
Regardless of the rule's origin, Ruff re-implements every rule in Rust as a first-party feature.
|
||||
|
||||
By default, Ruff enables Flake8's `E` and `F` rules. Ruff supports all rules from the `F` category,
|
||||
@@ -577,20 +540,20 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/) on PyPI.
|
||||
| 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 | |
|
||||
| F407 | future-feature-not-defined | Future feature `{name}` is not defined | |
|
||||
| F501 | percent-format-invalid-format | '...' % ... has invalid format string: {message} | |
|
||||
| F502 | percent-format-expected-mapping | '...' % ... expected mapping but got sequence | |
|
||||
| F503 | percent-format-expected-sequence | '...' % ... expected sequence but got mapping | |
|
||||
| F504 | percent-format-extra-named-arguments | '...' % ... has unused named argument(s): {message} | 🛠 |
|
||||
| F505 | percent-format-missing-argument | '...' % ... is missing argument(s) for placeholder(s): {message} | |
|
||||
| F506 | percent-format-mixed-positional-and-named | '...' % ... has mixed positional and named placeholders | |
|
||||
| F507 | percent-format-positional-count-mismatch | '...' % ... has {wanted} placeholder(s) but {got} substitution(s) | |
|
||||
| F508 | percent-format-star-requires-sequence | '...' % ... `*` specifier requires sequence | |
|
||||
| F509 | percent-format-unsupported-format-character | '...' % ... has unsupported format character '{char}' | |
|
||||
| F521 | string-dot-format-invalid-format | '...'.format(...) has invalid format string: {message} | |
|
||||
| F522 | string-dot-format-extra-named-arguments | '...'.format(...) has unused named argument(s): {message} | 🛠 |
|
||||
| F523 | string-dot-format-extra-positional-arguments | '...'.format(...) has unused arguments at position(s): {message} | |
|
||||
| F524 | string-dot-format-missing-arguments | '...'.format(...) is missing argument(s) for placeholder(s): {message} | |
|
||||
| F525 | string-dot-format-mixing-automatic | '...'.format(...) mixes automatic and manual numbering | |
|
||||
| F501 | percent-format-invalid-format | `%`-format string has invalid format string: {message} | |
|
||||
| F502 | percent-format-expected-mapping | `%`-format string expected mapping but got sequence | |
|
||||
| F503 | percent-format-expected-sequence | `%`-format string expected sequence but got mapping | |
|
||||
| F504 | percent-format-extra-named-arguments | `%`-format string has unused named argument(s): {message} | 🛠 |
|
||||
| F505 | percent-format-missing-argument | `%`-format string is missing argument(s) for placeholder(s): {message} | |
|
||||
| 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}' | |
|
||||
| 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 | 🛠 |
|
||||
| F601 | multi-value-repeated-key-literal | Dictionary key literal `{name}` repeated | 🛠 |
|
||||
| F602 | multi-value-repeated-key-variable | Dictionary key `{name}` repeated | 🛠 |
|
||||
@@ -826,6 +789,7 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/) on PyPI.
|
||||
| S106 | hardcoded-password-func-arg | Possible hardcoded password: "{}" | |
|
||||
| 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 | |
|
||||
| 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`: "{}" | |
|
||||
| S501 | request-with-no-cert-validation | Probable use of `{string}` call with `verify=False` disabling SSL certificate checks | |
|
||||
@@ -864,12 +828,12 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/) on PyPI
|
||||
| B004 | unreliable-callable-check | Using `hasattr(x, '__call__')` to test if x is callable is unreliable. Use `callable(x)` for consistent results. | |
|
||||
| B005 | strip-with-multi-characters | Using `.strip()` with multi-character strings is misleading the reader | |
|
||||
| B006 | mutable-argument-default | Do not use mutable data structures for argument defaults | |
|
||||
| B007 | unused-loop-control-variable | Loop control variable `{name}` not used within loop body | |
|
||||
| B007 | unused-loop-control-variable | Loop control variable `{name}` not used within loop body | 🛠 |
|
||||
| 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()` | 🛠 |
|
||||
| B012 | jump-statement-in-finally | `{name}` inside finally blocks cause exceptions to be silenced | |
|
||||
| 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. | |
|
||||
@@ -938,14 +902,14 @@ For more, see [flake8-datetimez](https://pypi.org/project/flake8-datetimez/) on
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| DTZ001 | call-datetime-without-tzinfo | The use of `datetime.datetime()` without `tzinfo` argument is not allowed | |
|
||||
| DTZ002 | call-datetime-today | The use of `datetime.datetime.today()` is not allowed | |
|
||||
| DTZ003 | call-datetime-utcnow | The use of `datetime.datetime.utcnow()` is not allowed | |
|
||||
| DTZ004 | call-datetime-utcfromtimestamp | The use of `datetime.datetime.utcfromtimestamp()` is not allowed | |
|
||||
| DTZ002 | call-datetime-today | The use of `datetime.datetime.today()` is not allowed, use `datetime.datetime.now(tz=)` instead | |
|
||||
| DTZ003 | call-datetime-utcnow | The use of `datetime.datetime.utcnow()` is not allowed, use `datetime.datetime.now(tz=)` instead | |
|
||||
| DTZ004 | call-datetime-utcfromtimestamp | The use of `datetime.datetime.utcfromtimestamp()` is not allowed, use `datetime.datetime.fromtimestamp(ts, tz=)` instead | |
|
||||
| DTZ005 | call-datetime-now-without-tzinfo | The use of `datetime.datetime.now()` without `tz` argument is not allowed | |
|
||||
| DTZ006 | call-datetime-fromtimestamp | The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed | |
|
||||
| DTZ007 | call-datetime-strptime-without-zone | The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` | |
|
||||
| DTZ011 | call-date-today | The use of `datetime.date.today()` is not allowed. | |
|
||||
| DTZ012 | call-date-fromtimestamp | The use of `datetime.date.fromtimestamp()` is not allowed | |
|
||||
| DTZ011 | call-date-today | The use of `datetime.date.today()` is not allowed, use `datetime.datetime.now(tz=).date()` instead | |
|
||||
| DTZ012 | call-date-fromtimestamp | The use of `datetime.date.fromtimestamp()` is not allowed, use `datetime.datetime.fromtimestamp(ts, tz=).date()` instead | |
|
||||
|
||||
### flake8-debugger (T10)
|
||||
|
||||
@@ -1248,6 +1212,8 @@ For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| 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 | |
|
||||
|
||||
#### Refactor (PLR)
|
||||
@@ -1294,8 +1260,12 @@ For more, see [tryceratops](https://pypi.org/project/tryceratops/1.1.0/) on PyPI
|
||||
|
||||
<!-- End auto-generated sections. -->
|
||||
|
||||
<!-- End section: Rules -->
|
||||
|
||||
## Editor Integrations
|
||||
|
||||
<!-- Begin section: Editor Integrations -->
|
||||
|
||||
### VS Code (Official)
|
||||
|
||||
Download the [Ruff VS Code extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff),
|
||||
@@ -1534,8 +1504,12 @@ jobs:
|
||||
run: ruff --format=github .
|
||||
```
|
||||
|
||||
<!-- End section: Editor Integrations -->
|
||||
|
||||
## FAQ
|
||||
|
||||
<!-- Begin section: FAQ -->
|
||||
|
||||
### Is Ruff compatible with Black?
|
||||
|
||||
Yes. Ruff is compatible with [Black](https://github.com/psf/black) out-of-the-box, as long as
|
||||
@@ -1578,9 +1552,9 @@ natively, including:
|
||||
- [`flake8-executable`](https://pypi.org/project/flake8-executable/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
|
||||
- [`flake8-logging-format`](https://pypi.org/project/flake8-logging-format/)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
@@ -1588,7 +1562,7 @@ natively, including:
|
||||
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [`flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [`flake8-type-checking`](https://pypi.org/project/flake8-type-checking/)
|
||||
- [`flake8-use-pathlib`](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
@@ -1630,6 +1604,21 @@ Unlike Pylint, Ruff is capable of automatically fixing its own lint violations.
|
||||
|
||||
Pylint parity is being tracked in [#970](https://github.com/charliermarsh/ruff/issues/970).
|
||||
|
||||
### How does Ruff compare to Mypy, or Pyright, or Pyre?
|
||||
|
||||
Ruff is a linter, not a type checker. It can detect some of the same problems that a type checker
|
||||
can, but a type checker will catch certain errors that Ruff would miss. The opposite is also true:
|
||||
Ruff will catch certain errors that a type checker would typically ignore.
|
||||
|
||||
For example, unlike a type checker, Ruff will notify you if an import is unused, by looking for
|
||||
references to that import in the source code; on the other hand, a type checker could flag that you
|
||||
passed an integer argument to a function that expects a string, which Ruff would miss. The
|
||||
tools are complementary.
|
||||
|
||||
It's recommended that you use Ruff in conjunction with a type checker, like Mypy, Pyright, or Pyre,
|
||||
with Ruff providing faster feedback on lint violations and the type checker providing more detailed
|
||||
feedback on type errors.
|
||||
|
||||
### Which tools does Ruff replace?
|
||||
|
||||
Today, Ruff can be used to replace Flake8 when used with any of the following plugins:
|
||||
@@ -1651,9 +1640,9 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-executable`](https://pypi.org/project/flake8-executable/)
|
||||
- [`flake8-implicit-str-concat`](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
|
||||
- [`flake8-logging-format`](https://pypi.org/project/flake8-logging-format/)
|
||||
- [`flake8-no-pep420`](https://pypi.org/project/flake8-no-pep420)
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/) ([#1543](https://github.com/charliermarsh/ruff/issues/1543))
|
||||
- [`flake8-pie`](https://pypi.org/project/flake8-pie/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
@@ -1661,7 +1650,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998))
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [`flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [`flake8-type-checking`](https://pypi.org/project/flake8-type-checking/)
|
||||
- [`flake8-use-pathlib`](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`pandas-vet`](https://pypi.org/project/pandas-vet/)
|
||||
@@ -1769,6 +1758,46 @@ matter how they're provided, which avoids accidental incompatibilities and simpl
|
||||
|
||||
Run `ruff /path/to/code.py --show-settings` to view the resolved settings for a given file.
|
||||
|
||||
### I want to use Ruff, but I don't want to use `pyproject.toml`. Is that possible?
|
||||
|
||||
Yes! In lieu of a `pyproject.toml` file, you can use a `ruff.toml` file for configuration. The two
|
||||
files are functionally equivalent and have an identical schema, with the exception that a `ruff.toml`
|
||||
file can omit the `[tool.ruff]` section header.
|
||||
|
||||
For example, given this `pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
convention = "google"
|
||||
```
|
||||
|
||||
You could instead use a `ruff.toml` file like so:
|
||||
|
||||
```toml
|
||||
line-length = 88
|
||||
|
||||
[pydocstyle]
|
||||
convention = "google"
|
||||
```
|
||||
|
||||
Ruff doesn't currently support INI files, like `setup.cfg` or `tox.ini`.
|
||||
|
||||
### How can I change Ruff's default configuration?
|
||||
|
||||
When no configuration file is found, Ruff will look for a user-specific `pyproject.toml` or
|
||||
`ruff.toml` file as a last resort. This behavior is similar to Flake8's `~/.config/flake8`.
|
||||
|
||||
On macOS, Ruff expects that file to be located at `/Users/Alice/Library/Application Support/ruff/ruff.toml`.
|
||||
|
||||
On Linux, Ruff expects that file to be located at `/home/alice/.config/ruff/ruff.toml`.
|
||||
|
||||
On Windows, Ruff expects that file to be located at `C:\Users\Alice\AppData\Roaming\ruff\ruff.toml`.
|
||||
|
||||
For more, see the [`dirs`](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) crate.
|
||||
|
||||
### Ruff tried to fix something, but it broke my code. What should I do?
|
||||
|
||||
Ruff's autofix is a best-effort mechanism. Given the dynamic nature of Python, it's difficult to
|
||||
@@ -1787,6 +1816,8 @@ unfixable = ["B", "SIM", "TRY", "RUF"]
|
||||
|
||||
If you find a case where Ruff's autofix breaks your code, please file an Issue!
|
||||
|
||||
<!-- End section: FAQ -->
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome and hugely appreciated. To get started, check out the
|
||||
@@ -1923,7 +1954,9 @@ Benchmark 1: find . -type f -name "*.py" | xargs -P 0 pyupgrade --py311-plus
|
||||
|
||||
## Reference
|
||||
|
||||
### Options
|
||||
<!-- Begin section: Settings -->
|
||||
|
||||
### Top-level
|
||||
|
||||
<!-- Sections automatically generated by `cargo dev generate-options`. -->
|
||||
<!-- Begin auto-generated options sections. -->
|
||||
@@ -2680,6 +2713,24 @@ suppress-none-returning = true
|
||||
|
||||
### `flake8-bandit`
|
||||
|
||||
#### [`check-typed-exception`](#check-typed-exception)
|
||||
|
||||
Whether to disallow `try`-`except`-`pass` (`S110`) for specific exception types. By default,
|
||||
`try`-`except`-`pass` is only disallowed for `Exception` and `BaseException`.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-bandit]
|
||||
check-typed-exception = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`hardcoded-tmp-directory`](#hardcoded-tmp-directory)
|
||||
|
||||
A list of directories to consider temporary.
|
||||
@@ -3118,7 +3169,7 @@ and can be circumvented via `eval` or `importlib`.
|
||||
Exempt certain modules from needing to be moved into type-checking
|
||||
blocks.
|
||||
|
||||
**Default value**: `[]`
|
||||
**Default value**: `["typing"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
@@ -3126,7 +3177,7 @@ blocks.
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-type-checking]
|
||||
exempt-modules = ["typing_extensions"]
|
||||
exempt-modules = ["typing", "typing_extensions"]
|
||||
```
|
||||
|
||||
---
|
||||
@@ -3666,6 +3717,8 @@ keep-runtime-typing = true
|
||||
|
||||
<!-- End auto-generated options sections. -->
|
||||
|
||||
<!-- End section: Settings -->
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
49
mkdocs.template.yml
Normal file
49
mkdocs.template.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
site_name: Ruff
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
- navigation.instant
|
||||
- navigation.tracking
|
||||
- content.code.annotate
|
||||
- toc.integrate
|
||||
- toc.follow
|
||||
- navigation.path
|
||||
- navigation.top
|
||||
- content.code.copy
|
||||
palette:
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: default
|
||||
primary: red
|
||||
toggle:
|
||||
icon: material/weather-sunny
|
||||
name: Switch to dark mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: slate
|
||||
primary: red
|
||||
toggle:
|
||||
icon: material/weather-night
|
||||
name: Switch to light mode
|
||||
repo_url: https://github.com/charliermarsh/ruff
|
||||
repo_name: ruff
|
||||
site_author: charliermarsh
|
||||
site_url: https://beta.ruff.rs/docs/
|
||||
site_dir: site/docs
|
||||
markdown_extensions:
|
||||
- toc:
|
||||
permalink: "#"
|
||||
- pymdownx.snippets:
|
||||
- pymdownx.magiclink:
|
||||
- attr_list:
|
||||
- md_in_html:
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
- pymdownx.inlinehilite:
|
||||
- pymdownx.superfences:
|
||||
- markdown.extensions.attr_list:
|
||||
- pymdownx.keys:
|
||||
- pymdownx.tasklist:
|
||||
custom_checkbox: true
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
plugins:
|
||||
- search
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
@@ -15,6 +15,7 @@ authors = [
|
||||
maintainers = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.7"
|
||||
license = { file = "LICENSE" }
|
||||
keywords = ["automation", "flake8", "pycodestyle", "pyflakes", "pylint", "clippy"]
|
||||
|
||||
@@ -39,3 +39,10 @@ class Foo:
|
||||
# Error
|
||||
def __init__(self, foo: int):
|
||||
...
|
||||
|
||||
|
||||
# Error – used to be ok for a moment since the mere presence
|
||||
# of a vararg falsely indicated that the function has a typed argument.
|
||||
class Foo:
|
||||
def __init__(self, *arg):
|
||||
...
|
||||
|
||||
14
resources/test/fixtures/flake8_bandit/S110.py
vendored
Normal file
14
resources/test/fixtures/flake8_bandit/S110.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -1,15 +1,26 @@
|
||||
# Errors
|
||||
"yoda" == compare # SIM300
|
||||
'yoda' == compare # SIM300
|
||||
"yoda" == compare # SIM300
|
||||
42 == age # SIM300
|
||||
("a", "b") == compare # SIM300
|
||||
"yoda" <= compare # SIM300
|
||||
'yoda' < compare # SIM300
|
||||
"yoda" < compare # SIM300
|
||||
42 > age # SIM300
|
||||
YODA == age # SIM300
|
||||
YODA > age # SIM300
|
||||
YODA >= age # SIM300
|
||||
JediOrder.YODA == age # SIM300
|
||||
|
||||
# OK
|
||||
compare == "yoda"
|
||||
age == 42
|
||||
compare == ("a", "b")
|
||||
x == y
|
||||
"yoda" == compare == 1
|
||||
"yoda" == compare == someothervar
|
||||
"yoda" == "yoda"
|
||||
age == YODA
|
||||
age < YODA
|
||||
age <= YODA
|
||||
YODA == YODA
|
||||
age == JediOrder.YODA
|
||||
|
||||
@@ -29,3 +29,4 @@ os.path.splitext(p)
|
||||
with open(p) as fp:
|
||||
fp.read()
|
||||
open(p).close()
|
||||
os.getcwdb(p)
|
||||
|
||||
18
resources/test/fixtures/pep8_naming/N806.py
vendored
18
resources/test/fixtures/pep8_naming/N806.py
vendored
@@ -6,7 +6,7 @@ from typing import NewType
|
||||
GLOBAL: str = "foo"
|
||||
|
||||
|
||||
def f():
|
||||
def assign():
|
||||
global GLOBAL
|
||||
GLOBAL = "bar"
|
||||
lower = 0
|
||||
@@ -18,4 +18,18 @@ def f():
|
||||
MyObj2 = namedtuple("MyObj12", ["a", "b"])
|
||||
|
||||
T = TypeVar("T")
|
||||
UserId = NewType('UserId', int)
|
||||
UserId = NewType("UserId", int)
|
||||
|
||||
|
||||
def aug_assign(rank, world_size):
|
||||
global CURRENT_PORT
|
||||
|
||||
CURRENT_PORT += 1
|
||||
if CURRENT_PORT > MAX_PORT:
|
||||
CURRENT_PORT = START_PORT
|
||||
|
||||
|
||||
def loop_assign():
|
||||
global CURRENT_PORT
|
||||
for CURRENT_PORT in range(5):
|
||||
pass
|
||||
|
||||
6
resources/test/fixtures/pyflakes/F401_8.py
vendored
Normal file
6
resources/test/fixtures/pyflakes/F401_8.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"""Test: explicit re-export of shadowed builtins."""
|
||||
|
||||
from concurrent.futures import (
|
||||
CancelledError as CancelledError,
|
||||
TimeoutError as TimeoutError,
|
||||
)
|
||||
11
resources/test/fixtures/pylint/PLE0604.py
vendored
Normal file
11
resources/test/fixtures/pylint/PLE0604.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
__all__ = (
|
||||
None, # [invalid-all-object]
|
||||
Fruit,
|
||||
Worm,
|
||||
)
|
||||
|
||||
class Fruit:
|
||||
pass
|
||||
|
||||
class Worm:
|
||||
pass
|
||||
7
resources/test/fixtures/pylint/PLE0605.py
vendored
Normal file
7
resources/test/fixtures/pylint/PLE0605.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
__all__ = ("CONST") # [invalid-all-format]
|
||||
|
||||
__all__ = ["Hello"] + {"world"} # [invalid-all-format]
|
||||
|
||||
__all__ += {"world"} # [invalid-all-format]
|
||||
|
||||
__all__ = {"world"} + ["Hello"] # [invalid-all-format]
|
||||
@@ -2,31 +2,37 @@
|
||||
# Errors.
|
||||
###
|
||||
def f():
|
||||
global x
|
||||
global X
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
global X
|
||||
|
||||
print(x)
|
||||
print(X)
|
||||
|
||||
|
||||
###
|
||||
# Non-errors.
|
||||
###
|
||||
def f():
|
||||
global x
|
||||
global X
|
||||
|
||||
x = 1
|
||||
X = 1
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
global X
|
||||
|
||||
(x, y) = (1, 2)
|
||||
(X, y) = (1, 2)
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
global X
|
||||
|
||||
del x
|
||||
del X
|
||||
|
||||
|
||||
def f():
|
||||
global X
|
||||
|
||||
X += 1
|
||||
|
||||
10
resources/test/fixtures/tryceratops/TRY400.py
vendored
10
resources/test/fixtures/tryceratops/TRY400.py
vendored
@@ -28,6 +28,16 @@ def bad():
|
||||
logger.error("Context message here")
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
except Exception:
|
||||
log.error("Context message here")
|
||||
|
||||
if True:
|
||||
log.error("Context message here")
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
|
||||
@@ -578,6 +578,13 @@
|
||||
"Flake8BanditOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"check-typed-exception": {
|
||||
"description": "Whether to disallow `try`-`except`-`pass` (`S110`) for specific exception types. By default, `try`-`except`-`pass` is only disallowed for `Exception` and `BaseException`.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hardcoded-tmp-directory": {
|
||||
"description": "A list of directories to consider temporary.",
|
||||
"type": [
|
||||
@@ -981,7 +988,7 @@
|
||||
"description": "Whether to place \"closer\" imports (fewer `.` characters, most local) before \"further\" imports (more `.` characters, least local), or vice versa.\n\nThe default (\"furthest-to-closest\") is equivalent to isort's `reverse-relative` default (`reverse-relative = false`); setting this to \"closest-to-furthest\" is equivalent to isort's `reverse-relative = true`.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/RelatveImportsOrder"
|
||||
"$ref": "#/definitions/RelativeImportsOrder"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
@@ -1197,7 +1204,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RelatveImportsOrder": {
|
||||
"RelativeImportsOrder": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Place \"closer\" imports (fewer `.` characters, most local) before \"further\" imports (more `.` characters, least local).",
|
||||
@@ -1615,6 +1622,10 @@
|
||||
"PLE011",
|
||||
"PLE0117",
|
||||
"PLE0118",
|
||||
"PLE06",
|
||||
"PLE060",
|
||||
"PLE0604",
|
||||
"PLE0605",
|
||||
"PLE1",
|
||||
"PLE11",
|
||||
"PLE114",
|
||||
@@ -1749,6 +1760,7 @@
|
||||
"S107",
|
||||
"S108",
|
||||
"S11",
|
||||
"S110",
|
||||
"S113",
|
||||
"S3",
|
||||
"S32",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -15,12 +15,44 @@ use rustc_hash::FxHashMap;
|
||||
#[command(
|
||||
author,
|
||||
name = "ruff",
|
||||
about = "Ruff: An extremely fast Python linter."
|
||||
about = "Ruff: An extremely fast Python linter.",
|
||||
after_help = "For help with a specific command, see: `ruff help <command>`."
|
||||
)]
|
||||
#[command(version)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Args {
|
||||
#[arg(required_unless_present_any = ["clean", "explain", "generate_shell_completion"])]
|
||||
#[command(subcommand)]
|
||||
pub command: Command,
|
||||
#[clap(flatten)]
|
||||
pub log_level_args: LogLevelArgs,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Command {
|
||||
/// Run Ruff on the given files or directories (default).
|
||||
Check(CheckArgs),
|
||||
/// Explain a rule.
|
||||
#[clap(alias = "--explain")]
|
||||
Rule {
|
||||
#[arg(value_parser=Rule::from_code)]
|
||||
rule: &'static Rule,
|
||||
|
||||
/// Output format
|
||||
#[arg(long, value_enum, default_value = "text")]
|
||||
format: HelpFormat,
|
||||
},
|
||||
/// Clear any caches in the current directory and any subdirectories.
|
||||
#[clap(alias = "--clean")]
|
||||
Clean,
|
||||
/// Generate shell completion.
|
||||
#[clap(alias = "--generate-shell-completion", hide = true)]
|
||||
GenerateShellCompletion { shell: clap_complete_command::Shell },
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Args)]
|
||||
#[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)]
|
||||
pub struct CheckArgs {
|
||||
/// List of files or directories to check.
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Attempt to automatically fix lint violations.
|
||||
#[arg(long, overrides_with("no_fix"))]
|
||||
@@ -183,9 +215,6 @@ pub struct Args {
|
||||
#[arg(
|
||||
long,
|
||||
// conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
@@ -193,64 +222,11 @@ pub struct Args {
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub add_noqa: bool,
|
||||
/// Explain a rule.
|
||||
#[arg(
|
||||
long,
|
||||
value_parser=Rule::from_code,
|
||||
help_heading="Subcommands",
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
// conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub explain: Option<&'static Rule>,
|
||||
/// Clear any caches in the current directory or any subdirectories.
|
||||
#[arg(
|
||||
long,
|
||||
help_heading="Subcommands",
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
// conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub clean: bool,
|
||||
/// Generate shell completion
|
||||
#[arg(
|
||||
long,
|
||||
hide = true,
|
||||
value_name = "SHELL",
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
// conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
/// See the files Ruff will be run against with the current settings.
|
||||
#[arg(
|
||||
long,
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
// conflicts_with = "show_files",
|
||||
conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
@@ -263,9 +239,6 @@ pub struct Args {
|
||||
long,
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
conflicts_with = "explain",
|
||||
conflicts_with = "generate_shell_completion",
|
||||
conflicts_with = "show_files",
|
||||
// conflicts_with = "show_settings",
|
||||
// Unsupported default-command arguments.
|
||||
@@ -273,22 +246,44 @@ pub struct Args {
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub show_settings: bool,
|
||||
#[clap(flatten)]
|
||||
pub log_level_args: LogLevelArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||
pub enum HelpFormat {
|
||||
Text,
|
||||
Json,
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, clap::Args)]
|
||||
pub struct LogLevelArgs {
|
||||
/// Enable verbose logging.
|
||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
global = true,
|
||||
group = "verbosity",
|
||||
help_heading = "Log levels"
|
||||
)]
|
||||
pub verbose: bool,
|
||||
/// Print lint violations, but nothing else.
|
||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
global = true,
|
||||
group = "verbosity",
|
||||
help_heading = "Log levels"
|
||||
)]
|
||||
pub quiet: bool,
|
||||
/// Disable all logging (but still exit with status code "1" upon detecting
|
||||
/// lint violations).
|
||||
#[arg(short, long, group = "verbosity", help_heading = "Log levels")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
global = true,
|
||||
group = "verbosity",
|
||||
help_heading = "Log levels"
|
||||
)]
|
||||
pub silent: bool,
|
||||
}
|
||||
|
||||
@@ -306,20 +301,17 @@ impl From<&LogLevelArgs> for LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
impl Args {
|
||||
impl CheckArgs {
|
||||
/// Partition the CLI into command-line arguments and configuration
|
||||
/// overrides.
|
||||
pub fn partition(self) -> (Arguments, Overrides) {
|
||||
(
|
||||
Arguments {
|
||||
add_noqa: self.add_noqa,
|
||||
clean: self.clean,
|
||||
config: self.config,
|
||||
diff: self.diff,
|
||||
exit_zero: self.exit_zero,
|
||||
explain: self.explain,
|
||||
files: self.files,
|
||||
generate_shell_completion: self.generate_shell_completion,
|
||||
isolated: self.isolated,
|
||||
no_cache: self.no_cache,
|
||||
show_files: self.show_files,
|
||||
@@ -371,13 +363,10 @@ fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Arguments {
|
||||
pub add_noqa: bool,
|
||||
pub clean: bool,
|
||||
pub config: Option<PathBuf>,
|
||||
pub diff: bool,
|
||||
pub exit_zero: bool,
|
||||
pub explain: Option<&'static Rule>,
|
||||
pub files: Vec<PathBuf>,
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
pub isolated: bool,
|
||||
pub no_cache: bool,
|
||||
pub show_files: bool,
|
||||
|
||||
@@ -18,12 +18,11 @@ use ruff::message::{Location, Message};
|
||||
use ruff::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff::resolver::PyprojectDiscovery;
|
||||
use ruff::settings::flags;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::args::Overrides;
|
||||
use crate::args::{HelpFormat, Overrides};
|
||||
use crate::cache;
|
||||
use crate::diagnostics::{lint_path, lint_stdin, Diagnostics};
|
||||
use crate::iterators::par_iter;
|
||||
@@ -269,10 +268,10 @@ struct Explanation<'a> {
|
||||
}
|
||||
|
||||
/// Explain a `Rule` to the user.
|
||||
pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
pub fn rule(rule: &Rule, format: HelpFormat) -> Result<()> {
|
||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||
match format {
|
||||
SerializationFormat::Text | SerializationFormat::Grouped => {
|
||||
HelpFormat::Text => {
|
||||
println!("{}\n", rule.as_ref());
|
||||
println!("Code: {} ({})\n", rule.code(), linter.name());
|
||||
|
||||
@@ -290,7 +289,7 @@ pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
println!("* {format}");
|
||||
}
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
HelpFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
@@ -300,24 +299,12 @@ pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
})?
|
||||
);
|
||||
}
|
||||
SerializationFormat::Junit => {
|
||||
bail!("`--explain` does not support junit format")
|
||||
}
|
||||
SerializationFormat::Github => {
|
||||
bail!("`--explain` does not support GitHub format")
|
||||
}
|
||||
SerializationFormat::Gitlab => {
|
||||
bail!("`--explain` does not support GitLab format")
|
||||
}
|
||||
SerializationFormat::Pylint => {
|
||||
bail!("`--explain` does not support pylint format")
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear any caches in the current directory or any subdirectories.
|
||||
pub fn clean(level: &LogLevel) -> Result<()> {
|
||||
pub fn clean(level: LogLevel) -> Result<()> {
|
||||
for entry in WalkDir::new(&*path_dedot::CWD)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
@@ -325,7 +312,7 @@ pub fn clean(level: &LogLevel) -> Result<()> {
|
||||
{
|
||||
let cache = entry.path().join(CACHE_DIR_NAME);
|
||||
if cache.is_dir() {
|
||||
if level >= &LogLevel::Default {
|
||||
if level >= LogLevel::Default {
|
||||
eprintln!("Removing cache at: {}", fs::relativize_path(&cache).bold());
|
||||
}
|
||||
remove_dir_all(&cache)?;
|
||||
|
||||
@@ -17,8 +17,8 @@ use ::ruff::resolver::PyprojectDiscovery;
|
||||
use ::ruff::settings::types::SerializationFormat;
|
||||
use ::ruff::{fix, fs, warn_user_once};
|
||||
use anyhow::Result;
|
||||
use args::Args;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use args::{Args, CheckArgs, Command};
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
use printer::{Printer, Violations};
|
||||
@@ -35,8 +35,28 @@ mod resolve;
|
||||
pub mod updates;
|
||||
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
let mut args: Vec<_> = std::env::args_os().collect();
|
||||
|
||||
// Clap doesn't support default subcommands but we want to run `check` by
|
||||
// default for convenience and backwards-compatibility, so we just
|
||||
// preprocess the arguments accordingly before passing them to Clap.
|
||||
if let Some(arg) = args.get(1).and_then(|s| s.to_str()) {
|
||||
if !Command::has_subcommand(rewrite_legacy_subcommand(arg))
|
||||
&& arg != "-h"
|
||||
&& arg != "--help"
|
||||
&& arg != "-V"
|
||||
&& arg != "--version"
|
||||
&& arg != "help"
|
||||
{
|
||||
args.insert(1, "check".into());
|
||||
}
|
||||
}
|
||||
|
||||
// Extract command-line arguments.
|
||||
let args = Args::parse();
|
||||
let Args {
|
||||
command,
|
||||
log_level_args,
|
||||
} = Args::parse_from(args);
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
@@ -53,20 +73,25 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
|
||||
let log_level: LogLevel = (&args.log_level_args).into();
|
||||
let log_level: LogLevel = (&log_level_args).into();
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
let (cli, overrides) = args.partition();
|
||||
match command {
|
||||
Command::Rule { rule, format } => commands::rule(rule, format)?,
|
||||
Command::Clean => commands::clean(log_level)?,
|
||||
Command::GenerateShellCompletion { shell } => {
|
||||
shell.generate(&mut Args::command(), &mut io::stdout());
|
||||
}
|
||||
|
||||
if let Some(shell) = cli.generate_shell_completion {
|
||||
shell.generate(&mut Args::command(), &mut io::stdout());
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.clean {
|
||||
commands::clean(&log_level)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
Command::Check(args) => return check(args, log_level),
|
||||
}
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
|
||||
fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitCode> {
|
||||
let (cli, overrides) = args.partition();
|
||||
|
||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||
// files are present, or files are injected from outside of the hierarchy.
|
||||
let pyproject_strategy = resolve::resolve(
|
||||
@@ -76,6 +101,14 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
cli.stdin_filename.as_deref(),
|
||||
)?;
|
||||
|
||||
if cli.show_settings {
|
||||
commands::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_files {
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
}
|
||||
|
||||
// Extract options that are included in `Settings`, but only apply at the top
|
||||
// level.
|
||||
let CliSettings {
|
||||
@@ -89,19 +122,6 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.cli.clone(),
|
||||
};
|
||||
|
||||
if let Some(rule) = cli.explain {
|
||||
commands::explain(rule, format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_settings {
|
||||
commands::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_files {
|
||||
commands::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Autofix rules are as follows:
|
||||
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or
|
||||
// print them to stdout, if we're reading from stdin).
|
||||
@@ -135,14 +155,17 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
warn_user_once!("Detected debug build without --no-cache.");
|
||||
}
|
||||
|
||||
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
||||
|
||||
if cli.add_noqa {
|
||||
let modifications = commands::add_noqa(&cli.files, &pyproject_strategy, &overrides)?;
|
||||
if modifications > 0 && log_level >= LogLevel::Default {
|
||||
println!("Added {modifications} noqa directives.");
|
||||
}
|
||||
} else if cli.watch {
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
let printer = Printer::new(&format, &log_level, &autofix, &violations);
|
||||
|
||||
if cli.watch {
|
||||
if !matches!(autofix, fix::FixMode::None) {
|
||||
warn_user_once!("--fix is not enabled in watch mode.");
|
||||
}
|
||||
@@ -244,10 +267,18 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
|
||||
fn rewrite_legacy_subcommand(cmd: &str) -> &str {
|
||||
match cmd {
|
||||
"--explain" => "rule",
|
||||
"--clean" => "clean",
|
||||
"--generate-shell-completion" => "generate-shell-completion",
|
||||
cmd => cmd,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn main() -> ExitCode {
|
||||
match inner_main() {
|
||||
|
||||
@@ -163,8 +163,8 @@ fn test_show_source() -> Result<()> {
|
||||
#[test]
|
||||
fn explain_status_codes() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--explain", "F401"]).assert().success();
|
||||
cmd.args(["--explain", "F401"]).assert().success();
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--explain", "RUF404"]).assert().failure();
|
||||
cmd.args(["--explain", "RUF404"]).assert().failure();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.236"
|
||||
version = "0.0.237"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
11
rustfmt.toml
11
rustfmt.toml
@@ -1,15 +1,4 @@
|
||||
condense_wildcard_suffixes = true
|
||||
edition = "2021"
|
||||
format_strings = true
|
||||
group_imports = "StdExternalCrate"
|
||||
hex_literal_case = "Lower"
|
||||
imports_granularity = "Module"
|
||||
max_width = 100
|
||||
normalize_comments = true
|
||||
normalize_doc_attributes = true
|
||||
reorder_impl_items = true
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
unstable_features = true
|
||||
use_field_init_shorthand = true
|
||||
wrap_comments = true
|
||||
|
||||
67
scripts/generate_mkdocs.py
Normal file
67
scripts/generate_mkdocs.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Generate an MkDocs-compatible `docs` and `mkdocs.yml` from the README.md."""
|
||||
import argparse
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
SECTIONS: list[tuple[str, str]] = [
|
||||
("Overview", "index.md"),
|
||||
("Installation and Usage", "installation-and-usage.md"),
|
||||
("Configuration", "configuration.md"),
|
||||
("Rules", "rules.md"),
|
||||
("Settings", "settings.md"),
|
||||
("Editor Integrations", "editor-integrations.md"),
|
||||
("FAQ", "faq.md"),
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Generate an MkDocs-compatible `docs` and `mkdocs.yml`."""
|
||||
with Path("README.md").open(encoding="utf8") as fp:
|
||||
content = fp.read()
|
||||
|
||||
Path("docs").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Split the README.md into sections.
|
||||
for (title, filename) in SECTIONS:
|
||||
with Path(f"docs/{filename}").open("w+") as f:
|
||||
block = content.split(f"<!-- Begin section: {title} -->")
|
||||
if len(block) != 2:
|
||||
msg = f"Section {title} not found in README.md"
|
||||
raise ValueError(msg)
|
||||
|
||||
block = block[1].split(f"<!-- End section: {title} -->")
|
||||
if len(block) != 2:
|
||||
msg = f"Section {title} not found in README.md"
|
||||
raise ValueError(msg)
|
||||
|
||||
f.write(block[0])
|
||||
|
||||
# Copy the CONTRIBUTING.md.
|
||||
shutil.copy("CONTRIBUTING.md", "docs/contributing.md")
|
||||
|
||||
# Add the nav section to mkdocs.yml.
|
||||
with Path("mkdocs.template.yml").open(encoding="utf8") as fp:
|
||||
config = yaml.safe_load(fp)
|
||||
config["nav"] = [
|
||||
{"Overview": "index.md"},
|
||||
{"Installation and Usage": "installation-and-usage.md"},
|
||||
{"Configuration": "configuration.md"},
|
||||
{"Rules": "rules.md"},
|
||||
{"Settings": "settings.md"},
|
||||
{"Editor Integrations": "editor-integrations.md"},
|
||||
{"FAQ": "faq.md"},
|
||||
{"Contributing": "contributing.md"},
|
||||
]
|
||||
|
||||
with Path("mkdocs.yml").open("w+") as fp:
|
||||
yaml.safe_dump(config, fp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate an MkDocs-compatible `docs` and `mkdocs.yml`.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
main()
|
||||
@@ -5,6 +5,7 @@ ignore = [
|
||||
"INP001", # implicit-namespace-package
|
||||
"PLR2004", # magic-value-comparison
|
||||
"S101", # assert-used
|
||||
"EM"
|
||||
]
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
|
||||
77
scripts/transform_readme.py
Normal file
77
scripts/transform_readme.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""Transform the README.md to support a specific deployment target.
|
||||
|
||||
By default, we assume that our README.md will be rendered on GitHub. However, different
|
||||
targets have different strategies for rendering light- and dark-mode images. This script
|
||||
adjusts the images in the README.md to support the given target.
|
||||
"""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
# https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to
|
||||
GITHUB = """
|
||||
<p align="center">
|
||||
<picture align="center">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/1309177/212613422-7faaf278-706b-4294-ad92-236ffcab3430.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
</picture>
|
||||
</p>
|
||||
"""
|
||||
|
||||
# https://github.com/pypi/warehouse/issues/11251
|
||||
PYPI = """
|
||||
<p align="center">
|
||||
<picture align="center">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
</picture>
|
||||
</p>
|
||||
"""
|
||||
|
||||
# https://squidfunk.github.io/mkdocs-material/reference/images/#light-and-dark-mode
|
||||
MK_DOCS = """
|
||||
<p align="center">
|
||||
<picture align="center">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg#only-light">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<picture align="center">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613422-7faaf278-706b-4294-ad92-236ffcab3430.svg#only-dark">
|
||||
</picture>
|
||||
</p>
|
||||
"""
|
||||
|
||||
|
||||
def main(target: str) -> None:
|
||||
"""Modify the README.md to support the given target."""
|
||||
with Path("README.md").open(encoding="utf8") as fp:
|
||||
content = fp.read()
|
||||
if GITHUB not in content:
|
||||
msg = "README.md is not in the expected format."
|
||||
raise ValueError(msg)
|
||||
|
||||
if target == "pypi":
|
||||
with Path("README.md").open("w", encoding="utf8") as fp:
|
||||
fp.write(content.replace(GITHUB, PYPI))
|
||||
elif target == "mkdocs":
|
||||
with Path("README.md").open("w", encoding="utf8") as fp:
|
||||
fp.write(content.replace(GITHUB, MK_DOCS))
|
||||
else:
|
||||
msg = f"Unknown target: {target}"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Modify the README.md to support a specific deployment target.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target",
|
||||
type=str,
|
||||
required=True,
|
||||
choices=("pypi", "mkdocs"),
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
main(target=args.target)
|
||||
@@ -1010,7 +1010,7 @@ pub fn is_logger_candidate(func: &Expr) -> bool {
|
||||
if let ExprKind::Attribute { value, .. } = &func.node {
|
||||
let call_path = collect_call_path(value);
|
||||
if let Some(tail) = call_path.last() {
|
||||
if *tail == "logging" || tail.ends_with("logger") {
|
||||
if tail.starts_with("log") || tail.ends_with("logger") || tail.ends_with("logging") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use bitflags::bitflags;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Cmpop, Located};
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||
@@ -9,9 +10,21 @@ use crate::ast::types::{Binding, BindingKind, Scope};
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct AllNamesFlags: u32 {
|
||||
const INVALID_FORMAT = 0b0000_0001;
|
||||
const INVALID_OBJECT = 0b0000_0010;
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the names bound to a given __all__ assignment.
|
||||
pub fn extract_all_names(stmt: &Stmt, scope: &Scope, bindings: &[Binding]) -> Vec<String> {
|
||||
fn add_to_names(names: &mut Vec<String>, elts: &[Expr]) {
|
||||
pub fn extract_all_names(
|
||||
stmt: &Stmt,
|
||||
scope: &Scope,
|
||||
bindings: &[Binding],
|
||||
) -> (Vec<String>, AllNamesFlags) {
|
||||
fn add_to_names(names: &mut Vec<String>, elts: &[Expr], flags: &mut AllNamesFlags) {
|
||||
for elt in elts {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
@@ -19,11 +32,14 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope, bindings: &[Binding]) -> Ve
|
||||
} = &elt.node
|
||||
{
|
||||
names.push(value.to_string());
|
||||
} else {
|
||||
*flags |= AllNamesFlags::INVALID_OBJECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut names: Vec<String> = vec![];
|
||||
let mut flags = AllNamesFlags::empty();
|
||||
|
||||
// Grab the existing bound __all__ values.
|
||||
if let StmtKind::AugAssign { .. } = &stmt.node {
|
||||
@@ -42,7 +58,7 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope, bindings: &[Binding]) -> Ve
|
||||
} {
|
||||
match &value.node {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
add_to_names(&mut names, elts);
|
||||
add_to_names(&mut names, elts, &mut flags);
|
||||
}
|
||||
ExprKind::BinOp { left, right, .. } => {
|
||||
let mut current_left = left;
|
||||
@@ -50,27 +66,35 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope, bindings: &[Binding]) -> Ve
|
||||
while let Some(elts) = match ¤t_right.node {
|
||||
ExprKind::List { elts, .. } => Some(elts),
|
||||
ExprKind::Tuple { elts, .. } => Some(elts),
|
||||
_ => None,
|
||||
_ => {
|
||||
flags |= AllNamesFlags::INVALID_FORMAT;
|
||||
None
|
||||
}
|
||||
} {
|
||||
add_to_names(&mut names, elts);
|
||||
add_to_names(&mut names, elts, &mut flags);
|
||||
match ¤t_left.node {
|
||||
ExprKind::BinOp { left, right, .. } => {
|
||||
current_left = left;
|
||||
current_right = right;
|
||||
}
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
add_to_names(&mut names, elts);
|
||||
add_to_names(&mut names, elts, &mut flags);
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
flags |= AllNamesFlags::INVALID_FORMAT;
|
||||
break;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
flags |= AllNamesFlags::INVALID_FORMAT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
names
|
||||
(names, flags)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -140,7 +140,7 @@ pub enum BindingKind<'a> {
|
||||
SubmoduleImportation(&'a str, &'a str),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Binding<'a> {
|
||||
pub kind: BindingKind<'a>,
|
||||
pub range: Range,
|
||||
@@ -161,7 +161,7 @@ pub struct Binding<'a> {
|
||||
pub synthetic_usage: Option<(usize, Range)>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub enum ExecutionContext {
|
||||
Runtime,
|
||||
Typing,
|
||||
|
||||
@@ -17,7 +17,7 @@ use rustpython_parser::parser;
|
||||
use smallvec::smallvec;
|
||||
|
||||
use crate::ast::helpers::{binding_range, collect_call_path, extract_handler_names};
|
||||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::operations::{extract_all_names, AllNamesFlags};
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingKind, CallPath, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range,
|
||||
@@ -3447,6 +3447,15 @@ where
|
||||
body,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::TryExceptPass) {
|
||||
flake8_bandit::rules::try_except_pass(
|
||||
self,
|
||||
type_.as_deref(),
|
||||
name.as_deref(),
|
||||
body,
|
||||
self.settings.flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ReraiseNoCause) {
|
||||
tryceratops::rules::reraise_no_cause(self, body);
|
||||
}
|
||||
@@ -3723,7 +3732,7 @@ impl<'a> Checker<'a> {
|
||||
kind: BindingKind::Builtin,
|
||||
range: Range::default(),
|
||||
runtime_usage: None,
|
||||
synthetic_usage: None,
|
||||
synthetic_usage: Some((0, Range::default())),
|
||||
typing_usage: None,
|
||||
source: None,
|
||||
context: ExecutionContext::Runtime,
|
||||
@@ -3850,16 +3859,41 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Assume the rebound name is used as a global or within a loop.
|
||||
let scope = self.current_scope();
|
||||
let binding = match scope.values.get(&name) {
|
||||
None => binding,
|
||||
Some(index) => Binding {
|
||||
runtime_usage: self.bindings[*index].runtime_usage,
|
||||
synthetic_usage: self.bindings[*index].synthetic_usage,
|
||||
typing_usage: self.bindings[*index].typing_usage,
|
||||
..binding
|
||||
},
|
||||
let binding = if let Some(index) = scope.values.get(&name) {
|
||||
if matches!(self.bindings[*index].kind, BindingKind::Builtin) {
|
||||
// Avoid overriding builtins.
|
||||
binding
|
||||
} else if matches!(self.bindings[*index].kind, BindingKind::Global) {
|
||||
// If the original binding was a global, and the new binding conflicts within the
|
||||
// current scope, then the new binding is also a global.
|
||||
Binding {
|
||||
runtime_usage: self.bindings[*index].runtime_usage,
|
||||
synthetic_usage: self.bindings[*index].synthetic_usage,
|
||||
typing_usage: self.bindings[*index].typing_usage,
|
||||
kind: BindingKind::Global,
|
||||
..binding
|
||||
}
|
||||
} else if matches!(self.bindings[*index].kind, BindingKind::Nonlocal) {
|
||||
// If the original binding was a nonlocal, and the new binding conflicts within the
|
||||
// current scope, then the new binding is also a nonlocal.
|
||||
Binding {
|
||||
runtime_usage: self.bindings[*index].runtime_usage,
|
||||
synthetic_usage: self.bindings[*index].synthetic_usage,
|
||||
typing_usage: self.bindings[*index].typing_usage,
|
||||
kind: BindingKind::Nonlocal,
|
||||
..binding
|
||||
}
|
||||
} else {
|
||||
Binding {
|
||||
runtime_usage: self.bindings[*index].runtime_usage,
|
||||
synthetic_usage: self.bindings[*index].synthetic_usage,
|
||||
typing_usage: self.bindings[*index].typing_usage,
|
||||
..binding
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding
|
||||
};
|
||||
|
||||
// Don't treat annotations as assignments if there is an existing value
|
||||
@@ -4152,14 +4186,25 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
_ => false,
|
||||
} {
|
||||
let (all_names, all_names_flags) =
|
||||
extract_all_names(parent, current, &self.bindings);
|
||||
|
||||
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::InvalidAllObject)
|
||||
&& matches!(all_names_flags, AllNamesFlags::INVALID_OBJECT)
|
||||
{
|
||||
pylint::rules::invalid_all_object(self, expr);
|
||||
}
|
||||
|
||||
self.add_binding(
|
||||
id,
|
||||
Binding {
|
||||
kind: BindingKind::Export(extract_all_names(
|
||||
parent,
|
||||
current,
|
||||
&self.bindings,
|
||||
)),
|
||||
kind: BindingKind::Export(all_names),
|
||||
runtime_usage: None,
|
||||
synthetic_usage: None,
|
||||
typing_usage: None,
|
||||
@@ -4438,10 +4483,14 @@ impl<'a> Checker<'a> {
|
||||
for (name, index) in &scope.values {
|
||||
let binding = &self.bindings[*index];
|
||||
if matches!(binding.kind, BindingKind::Global) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::GlobalVariableNotAssigned((*name).to_string()),
|
||||
binding.range,
|
||||
));
|
||||
if let Some(stmt) = &binding.source {
|
||||
if matches!(stmt.node, StmtKind::Global { .. }) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::GlobalVariableNotAssigned((*name).to_string()),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4703,7 +4752,11 @@ impl<'a> Checker<'a> {
|
||||
let multiple = unused_imports.len() > 1;
|
||||
for (full_name, range) in unused_imports {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedImport(full_name.to_string(), ignore_init, multiple),
|
||||
violations::UnusedImport {
|
||||
name: full_name.to_string(),
|
||||
ignore_init,
|
||||
multiple,
|
||||
},
|
||||
*range,
|
||||
);
|
||||
if matches!(child.node, StmtKind::ImportFrom { .. })
|
||||
@@ -4725,7 +4778,11 @@ impl<'a> Checker<'a> {
|
||||
let multiple = unused_imports.len() > 1;
|
||||
for (full_name, range) in unused_imports {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedImport(full_name.to_string(), ignore_init, multiple),
|
||||
violations::UnusedImport {
|
||||
name: full_name.to_string(),
|
||||
ignore_init,
|
||||
multiple,
|
||||
},
|
||||
*range,
|
||||
);
|
||||
if matches!(child.node, StmtKind::ImportFrom { .. })
|
||||
|
||||
@@ -46,7 +46,7 @@ macro_rules! notify_user {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum LogLevel {
|
||||
// No output (+ `log::LevelFilter::Off`).
|
||||
Silent,
|
||||
@@ -60,6 +60,7 @@ pub enum LogLevel {
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn level_filter(&self) -> log::LevelFilter {
|
||||
match self {
|
||||
LogLevel::Default => log::LevelFilter::Info,
|
||||
|
||||
@@ -49,7 +49,7 @@ pub static TYPING_EXTENSIONS: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
|
||||
"assert_type",
|
||||
"clear_overloads",
|
||||
"final",
|
||||
"get_Type_hints",
|
||||
"get_type_hints",
|
||||
"get_args",
|
||||
"get_origin",
|
||||
"get_overloads",
|
||||
|
||||
@@ -78,6 +78,8 @@ ruff_macros::define_rule_mapping!(
|
||||
F842 => violations::UnusedAnnotation,
|
||||
F901 => violations::RaiseNotImplemented,
|
||||
// pylint
|
||||
PLE0604 => rules::pylint::rules::InvalidAllObject,
|
||||
PLE0605 => rules::pylint::rules::InvalidAllFormat,
|
||||
PLC0414 => violations::UselessImportAlias,
|
||||
PLC3002 => violations::UnnecessaryDirectLambdaCall,
|
||||
PLE0117 => violations::NonlocalWithoutBinding,
|
||||
@@ -331,6 +333,7 @@ ruff_macros::define_rule_mapping!(
|
||||
S106 => violations::HardcodedPasswordFuncArg,
|
||||
S107 => violations::HardcodedPasswordDefault,
|
||||
S108 => violations::HardcodedTempFile,
|
||||
S110 => rules::flake8_bandit::rules::TryExceptPass,
|
||||
S113 => violations::RequestWithoutTimeout,
|
||||
S324 => violations::HashlibInsecureHashFunction,
|
||||
S501 => violations::RequestWithNoCertValidation,
|
||||
@@ -732,12 +735,20 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
/// Pairs of checks that shouldn't be enabled together.
|
||||
pub const INCOMPATIBLE_CODES: &[(Rule, Rule, &str)] = &[(
|
||||
Rule::OneBlankLineBeforeClass,
|
||||
Rule::NoBlankLineBeforeClass,
|
||||
"`D203` (OneBlankLineBeforeClass) and `D211` (NoBlankLinesBeforeClass) are incompatible. \
|
||||
Consider adding `D203` to `ignore`.",
|
||||
)];
|
||||
pub const INCOMPATIBLE_CODES: &[(Rule, Rule, &str)] = &[
|
||||
(
|
||||
Rule::OneBlankLineBeforeClass,
|
||||
Rule::NoBlankLineBeforeClass,
|
||||
"`one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are \
|
||||
incompatible. Consider ignoring `one-blank-line-before-class`.",
|
||||
),
|
||||
(
|
||||
Rule::MultiLineSummaryFirstLine,
|
||||
Rule::MultiLineSummarySecondLine,
|
||||
"`multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are \
|
||||
incompatible. Consider ignoring one.",
|
||||
),
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@@ -125,8 +125,8 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
|
||||
// ANN002, ANN401
|
||||
if let Some(arg) = &args.vararg {
|
||||
has_any_typed_arg = true;
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
has_any_typed_arg = true;
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker
|
||||
.settings
|
||||
@@ -153,8 +153,8 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
|
||||
// ANN003, ANN401
|
||||
if let Some(arg) = &args.kwarg {
|
||||
has_any_typed_arg = true;
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
has_any_typed_arg = true;
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker
|
||||
.settings
|
||||
|
||||
@@ -48,4 +48,22 @@ expression: diagnostics
|
||||
column: 12
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
MissingReturnTypeSpecialMethod: __init__
|
||||
location:
|
||||
row: 47
|
||||
column: 8
|
||||
end_location:
|
||||
row: 47
|
||||
column: 16
|
||||
fix:
|
||||
content:
|
||||
- " -> None"
|
||||
location:
|
||||
row: 47
|
||||
column: 28
|
||||
end_location:
|
||||
row: 47
|
||||
column: 28
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ mod tests {
|
||||
#[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"); "S509")]
|
||||
#[test_case(Rule::LoggingConfigInsecureListen, Path::new("S612.py"); "S612")]
|
||||
#[test_case(Rule::Jinja2AutoescapeFalse, Path::new("S701.py"); "S701")]
|
||||
#[test_case(Rule::TryExceptPass, Path::new("S110.py"); "S110")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
@@ -55,6 +56,7 @@ mod tests {
|
||||
"/dev/shm".to_string(),
|
||||
"/foo".to_string(),
|
||||
],
|
||||
check_typed_exception: false,
|
||||
},
|
||||
..Settings::for_rule(Rule::HardcodedTempFile)
|
||||
},
|
||||
@@ -62,4 +64,20 @@ mod tests {
|
||||
assert_yaml_snapshot!("S108_extend", diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_typed_exception() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bandit/S110.py"),
|
||||
&Settings {
|
||||
flake8_bandit: super::settings::Settings {
|
||||
check_typed_exception: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Settings::for_rule(Rule::TryExceptPass)
|
||||
},
|
||||
)?;
|
||||
assert_yaml_snapshot!("S110_typed", diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub use request_with_no_cert_validation::request_with_no_cert_validation;
|
||||
pub use request_without_timeout::request_without_timeout;
|
||||
pub use snmp_insecure_version::snmp_insecure_version;
|
||||
pub use snmp_weak_cryptography::snmp_weak_cryptography;
|
||||
pub use try_except_pass::{try_except_pass, TryExceptPass};
|
||||
pub use unsafe_yaml_load::unsafe_yaml_load;
|
||||
|
||||
mod assert_used;
|
||||
@@ -34,4 +35,5 @@ mod request_with_no_cert_validation;
|
||||
mod request_without_timeout;
|
||||
mod snmp_insecure_version;
|
||||
mod snmp_weak_cryptography;
|
||||
mod try_except_pass;
|
||||
mod unsafe_yaml_load;
|
||||
|
||||
43
src/rules/flake8_bandit/rules/try_except_pass.rs
Normal file
43
src/rules/flake8_bandit/rules/try_except_pass.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct TryExceptPass;
|
||||
);
|
||||
impl Violation for TryExceptPass {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`try`-`except`-`pass` detected, consider logging the exception")
|
||||
}
|
||||
}
|
||||
|
||||
/// S110
|
||||
pub fn try_except_pass(
|
||||
checker: &mut Checker,
|
||||
type_: Option<&Expr>,
|
||||
_name: Option<&str>,
|
||||
body: &[Stmt],
|
||||
check_typed_exception: bool,
|
||||
) {
|
||||
if body.len() == 1
|
||||
&& body[0].node == StmtKind::Pass
|
||||
&& (check_typed_exception
|
||||
|| type_.map_or(true, |type_| {
|
||||
checker.resolve_call_path(type_).map_or(true, |call_path| {
|
||||
call_path.as_slice() == ["", "Exception"]
|
||||
|| call_path.as_slice() == ["", "BaseException"]
|
||||
})
|
||||
}))
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
TryExceptPass,
|
||||
Range::from_located(&body[0]),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,20 @@ pub struct Options {
|
||||
/// A list of directories to consider temporary, in addition to those
|
||||
/// specified by `hardcoded-tmp-directory`.
|
||||
pub hardcoded_tmp_directory_extend: Option<Vec<String>>,
|
||||
#[option(
|
||||
default = "false",
|
||||
value_type = "bool",
|
||||
example = "check-typed-exception = true"
|
||||
)]
|
||||
/// Whether to disallow `try`-`except`-`pass` (`S110`) for specific exception types. By default,
|
||||
/// `try`-`except`-`pass` is only disallowed for `Exception` and `BaseException`.
|
||||
pub check_typed_exception: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct Settings {
|
||||
pub hardcoded_tmp_directory: Vec<String>,
|
||||
pub check_typed_exception: bool,
|
||||
}
|
||||
|
||||
impl From<Options> for Settings {
|
||||
@@ -55,6 +64,7 @@ impl From<Options> for Settings {
|
||||
.into_iter(),
|
||||
)
|
||||
.collect(),
|
||||
check_typed_exception: options.check_typed_exception.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +74,7 @@ impl From<Settings> for Options {
|
||||
Self {
|
||||
hardcoded_tmp_directory: Some(settings.hardcoded_tmp_directory),
|
||||
hardcoded_tmp_directory_extend: None,
|
||||
check_typed_exception: Some(settings.check_typed_exception),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +83,7 @@ impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hardcoded_tmp_directory: default_tmp_dirs(),
|
||||
check_typed_exception: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source: src/rules/flake8_bandit/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
TryExceptPass: ~
|
||||
location:
|
||||
row: 4
|
||||
column: 4
|
||||
end_location:
|
||||
row: 4
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TryExceptPass: ~
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
end_location:
|
||||
row: 9
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: src/rules/flake8_bandit/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
TryExceptPass: ~
|
||||
location:
|
||||
row: 4
|
||||
column: 4
|
||||
end_location:
|
||||
row: 4
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TryExceptPass: ~
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
end_location:
|
||||
row: 9
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
TryExceptPass: ~
|
||||
location:
|
||||
row: 14
|
||||
column: 4
|
||||
end_location:
|
||||
row: 14
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -3,9 +3,21 @@ use rustpython_ast::{Cmpop, Expr, ExprKind};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::python::string::{self};
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
|
||||
/// Return `true` if an [`Expr`] is a constant or a constant-like name.
|
||||
fn is_constant_like(expr: &Expr) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Attribute { attr, .. } => string::is_upper(attr),
|
||||
ExprKind::Constant { .. } => true,
|
||||
ExprKind::Tuple { elts, .. } => elts.iter().all(is_constant_like),
|
||||
ExprKind::Name { id, .. } => string::is_upper(id),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// SIM300
|
||||
pub fn yoda_conditions(
|
||||
checker: &mut Checker,
|
||||
@@ -24,10 +36,8 @@ pub fn yoda_conditions(
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if !matches!(&left.node, &ExprKind::Constant { .. }) {
|
||||
return;
|
||||
}
|
||||
if matches!(&right.node, &ExprKind::Constant { .. }) {
|
||||
|
||||
if !is_constant_like(left) || is_constant_like(right) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: "compare == 'yoda'"
|
||||
suggestion: "compare == \"yoda\""
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
@@ -32,7 +32,7 @@ expression: diagnostics
|
||||
column: 17
|
||||
fix:
|
||||
content:
|
||||
- "compare == 'yoda'"
|
||||
- "compare == \"yoda\""
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
@@ -61,59 +61,154 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: "compare >= \"yoda\""
|
||||
suggestion: "compare == (\"a\", \"b\")"
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 21
|
||||
fix:
|
||||
content:
|
||||
- "compare == (\"a\", \"b\")"
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 21
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: "compare >= \"yoda\""
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 17
|
||||
fix:
|
||||
content:
|
||||
- "compare >= \"yoda\""
|
||||
location:
|
||||
row: 5
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
row: 6
|
||||
column: 17
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: "compare > 'yoda'"
|
||||
suggestion: "compare > \"yoda\""
|
||||
location:
|
||||
row: 6
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
row: 7
|
||||
column: 16
|
||||
fix:
|
||||
content:
|
||||
- "compare > 'yoda'"
|
||||
- "compare > \"yoda\""
|
||||
location:
|
||||
row: 6
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
row: 7
|
||||
column: 16
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: age < 42
|
||||
location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 8
|
||||
fix:
|
||||
content:
|
||||
- age < 42
|
||||
location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 8
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: age == YODA
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 11
|
||||
fix:
|
||||
content:
|
||||
- age == YODA
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 11
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: age < YODA
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 10
|
||||
fix:
|
||||
content:
|
||||
- age < YODA
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 10
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: age <= YODA
|
||||
location:
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 11
|
||||
column: 11
|
||||
fix:
|
||||
content:
|
||||
- age <= YODA
|
||||
location:
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 11
|
||||
column: 11
|
||||
parent: ~
|
||||
- kind:
|
||||
YodaConditions:
|
||||
suggestion: age == JediOrder.YODA
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 21
|
||||
fix:
|
||||
content:
|
||||
- age == JediOrder.YODA
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 21
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ pub struct Options {
|
||||
/// See: https://github.com/snok/flake8-type-checking#strict.
|
||||
pub strict: Option<bool>,
|
||||
#[option(
|
||||
default = "[]",
|
||||
default = "[\"typing\"]",
|
||||
value_type = "Vec<String>",
|
||||
example = r#"
|
||||
exempt-modules = ["typing_extensions"]
|
||||
exempt-modules = ["typing", "typing_extensions"]
|
||||
"#
|
||||
)]
|
||||
/// Exempt certain modules from needing to be moved into type-checking
|
||||
@@ -36,17 +36,28 @@ pub struct Options {
|
||||
pub exempt_modules: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Default)]
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct Settings {
|
||||
pub strict: bool,
|
||||
pub exempt_modules: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
strict: false,
|
||||
exempt_modules: vec!["typing".to_string()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Options> for Settings {
|
||||
fn from(options: Options) -> Self {
|
||||
Self {
|
||||
strict: options.strict.unwrap_or_default(),
|
||||
exempt_modules: options.exempt_modules.unwrap_or_default(),
|
||||
strict: options.strict.unwrap_or(false),
|
||||
exempt_modules: options
|
||||
.exempt_modules
|
||||
.unwrap_or_else(|| vec!["typing".to_string()]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ pub fn replaceable_by_pathlib(checker: &mut Checker, expr: &Expr) {
|
||||
["os", "remove"] => Some(PathlibRemove.into()),
|
||||
["os", "unlink"] => Some(PathlibUnlink.into()),
|
||||
["os", "getcwd"] => Some(PathlibGetcwd.into()),
|
||||
["os", "getcwdb"] => Some(PathlibGetcwd.into()),
|
||||
["os", "path", "exists"] => Some(PathlibExists.into()),
|
||||
["os", "path", "expanduser"] => Some(PathlibExpanduser.into()),
|
||||
["os", "path", "isdir"] => Some(PathlibIsDir.into()),
|
||||
|
||||
@@ -252,4 +252,14 @@ expression: diagnostics
|
||||
column: 4
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
PathlibGetcwd: ~
|
||||
location:
|
||||
row: 32
|
||||
column: 0
|
||||
end_location:
|
||||
row: 32
|
||||
column: 10
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use itertools::Either::{Left, Right};
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
use settings::RelatveImportsOrder;
|
||||
use settings::RelativeImportsOrder;
|
||||
use sorting::{cmp_either_import, cmp_import_from, cmp_members, cmp_modules};
|
||||
use track::{Block, Trailer};
|
||||
use types::EitherImport::{Import, ImportFrom};
|
||||
@@ -386,7 +386,7 @@ fn categorize_imports<'a>(
|
||||
fn order_imports<'a>(
|
||||
block: ImportBlock<'a>,
|
||||
order_by_type: bool,
|
||||
relative_imports_order: RelatveImportsOrder,
|
||||
relative_imports_order: RelativeImportsOrder,
|
||||
classes: &'a BTreeSet<String>,
|
||||
constants: &'a BTreeSet<String>,
|
||||
variables: &'a BTreeSet<String>,
|
||||
@@ -568,7 +568,7 @@ pub fn format_imports(
|
||||
known_first_party: &BTreeSet<String>,
|
||||
known_third_party: &BTreeSet<String>,
|
||||
order_by_type: bool,
|
||||
relative_imports_order: RelatveImportsOrder,
|
||||
relative_imports_order: RelativeImportsOrder,
|
||||
single_line_exclusions: &BTreeSet<String>,
|
||||
split_on_trailing_comma: bool,
|
||||
classes: &BTreeSet<String>,
|
||||
@@ -681,7 +681,7 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use super::categorize::ImportType;
|
||||
use super::settings::RelatveImportsOrder;
|
||||
use super::settings::RelativeImportsOrder;
|
||||
use crate::assert_yaml_snapshot;
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
@@ -1082,7 +1082,7 @@ mod tests {
|
||||
.as_path(),
|
||||
&Settings {
|
||||
isort: super::settings::Settings {
|
||||
relative_imports_order: RelatveImportsOrder::ClosestToFurthest,
|
||||
relative_imports_order: RelativeImportsOrder::ClosestToFurthest,
|
||||
..super::settings::Settings::default()
|
||||
},
|
||||
src: vec![Path::new("resources/test/fixtures/isort").to_path_buf()],
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::categorize::ImportType;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub enum RelatveImportsOrder {
|
||||
pub enum RelativeImportsOrder {
|
||||
/// Place "closer" imports (fewer `.` characters, most local) before
|
||||
/// "further" imports (more `.` characters, least local).
|
||||
ClosestToFurthest,
|
||||
@@ -19,7 +19,7 @@ pub enum RelatveImportsOrder {
|
||||
FurthestToClosest,
|
||||
}
|
||||
|
||||
impl Default for RelatveImportsOrder {
|
||||
impl Default for RelativeImportsOrder {
|
||||
fn default() -> Self {
|
||||
Self::FurthestToClosest
|
||||
}
|
||||
@@ -163,7 +163,7 @@ pub struct Options {
|
||||
/// `reverse-relative` default (`reverse-relative = false`); setting
|
||||
/// this to "closest-to-furthest" is equivalent to isort's `reverse-relative
|
||||
/// = true`.
|
||||
pub relative_imports_order: Option<RelatveImportsOrder>,
|
||||
pub relative_imports_order: Option<RelativeImportsOrder>,
|
||||
#[option(
|
||||
default = r#"[]"#,
|
||||
value_type = "Vec<String>",
|
||||
@@ -227,7 +227,7 @@ pub struct Settings {
|
||||
pub known_first_party: BTreeSet<String>,
|
||||
pub known_third_party: BTreeSet<String>,
|
||||
pub order_by_type: bool,
|
||||
pub relative_imports_order: RelatveImportsOrder,
|
||||
pub relative_imports_order: RelativeImportsOrder,
|
||||
pub single_line_exclusions: BTreeSet<String>,
|
||||
pub split_on_trailing_comma: bool,
|
||||
pub classes: BTreeSet<String>,
|
||||
@@ -248,7 +248,7 @@ impl Default for Settings {
|
||||
known_first_party: BTreeSet::new(),
|
||||
known_third_party: BTreeSet::new(),
|
||||
order_by_type: true,
|
||||
relative_imports_order: RelatveImportsOrder::default(),
|
||||
relative_imports_order: RelativeImportsOrder::default(),
|
||||
single_line_exclusions: BTreeSet::new(),
|
||||
split_on_trailing_comma: true,
|
||||
classes: BTreeSet::new(),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use super::settings::RelatveImportsOrder;
|
||||
use super::settings::RelativeImportsOrder;
|
||||
use super::types::EitherImport::{Import, ImportFrom};
|
||||
use super::types::{AliasData, EitherImport, ImportFromData};
|
||||
use crate::python::string;
|
||||
@@ -75,15 +75,15 @@ pub fn cmp_members(
|
||||
pub fn cmp_levels(
|
||||
level1: Option<&usize>,
|
||||
level2: Option<&usize>,
|
||||
relative_imports_order: RelatveImportsOrder,
|
||||
relative_imports_order: RelativeImportsOrder,
|
||||
) -> Ordering {
|
||||
match (level1, level2) {
|
||||
(None, None) => Ordering::Equal,
|
||||
(None, Some(_)) => Ordering::Less,
|
||||
(Some(_), None) => Ordering::Greater,
|
||||
(Some(level1), Some(level2)) => match relative_imports_order {
|
||||
RelatveImportsOrder::ClosestToFurthest => level1.cmp(level2),
|
||||
RelatveImportsOrder::FurthestToClosest => level2.cmp(level1),
|
||||
RelativeImportsOrder::ClosestToFurthest => level1.cmp(level2),
|
||||
RelativeImportsOrder::FurthestToClosest => level2.cmp(level1),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ pub fn cmp_levels(
|
||||
pub fn cmp_import_from(
|
||||
import_from1: &ImportFromData,
|
||||
import_from2: &ImportFromData,
|
||||
relative_imports_order: RelatveImportsOrder,
|
||||
relative_imports_order: RelativeImportsOrder,
|
||||
) -> Ordering {
|
||||
cmp_levels(
|
||||
import_from1.level,
|
||||
@@ -113,7 +113,7 @@ pub fn cmp_import_from(
|
||||
pub fn cmp_either_import(
|
||||
a: &EitherImport,
|
||||
b: &EitherImport,
|
||||
relative_imports_order: RelatveImportsOrder,
|
||||
relative_imports_order: RelativeImportsOrder,
|
||||
) -> Ordering {
|
||||
match (a, b) {
|
||||
(Import((alias1, _)), Import((alias2, _))) => cmp_modules(alias1, alias2),
|
||||
|
||||
@@ -28,6 +28,7 @@ mod tests {
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_5.py"); "F401_5")]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_6.py"); "F401_6")]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_7.py"); "F401_7")]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_8.py"); "F401_8")]
|
||||
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.py"); "F402")]
|
||||
#[test_case(Rule::ImportStarUsed, Path::new("F403.py"); "F403")]
|
||||
#[test_case(Rule::LateFutureImport, Path::new("F404.py"); "F404")]
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- functools
|
||||
- false
|
||||
- false
|
||||
name: functools
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 2
|
||||
column: 7
|
||||
@@ -25,9 +25,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- collections.OrderedDict
|
||||
- false
|
||||
- false
|
||||
name: collections.OrderedDict
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 6
|
||||
column: 4
|
||||
@@ -51,9 +51,9 @@ expression: diagnostics
|
||||
column: 0
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- logging.handlers
|
||||
- false
|
||||
- false
|
||||
name: logging.handlers
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 12
|
||||
column: 7
|
||||
@@ -72,9 +72,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- shelve
|
||||
- false
|
||||
- false
|
||||
name: shelve
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 32
|
||||
column: 11
|
||||
@@ -93,9 +93,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- importlib
|
||||
- false
|
||||
- false
|
||||
name: importlib
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 33
|
||||
column: 11
|
||||
@@ -114,9 +114,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- pathlib
|
||||
- false
|
||||
- false
|
||||
name: pathlib
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 37
|
||||
column: 11
|
||||
@@ -135,9 +135,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- pickle
|
||||
- false
|
||||
- false
|
||||
name: pickle
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 52
|
||||
column: 15
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- a.b.c
|
||||
- false
|
||||
- false
|
||||
name: a.b.c
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 2
|
||||
column: 16
|
||||
@@ -25,9 +25,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- d.e.f
|
||||
- false
|
||||
- false
|
||||
name: d.e.f
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 3
|
||||
column: 16
|
||||
@@ -46,9 +46,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- h.i
|
||||
- false
|
||||
- false
|
||||
name: h.i
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 4
|
||||
column: 7
|
||||
@@ -67,9 +67,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- j.k
|
||||
- false
|
||||
- false
|
||||
name: j.k
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- ".background.BackgroundTasks"
|
||||
- false
|
||||
- false
|
||||
name: ".background.BackgroundTasks"
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 7
|
||||
column: 24
|
||||
@@ -25,9 +25,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- ".datastructures.UploadFile"
|
||||
- false
|
||||
- false
|
||||
name: ".datastructures.UploadFile"
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 10
|
||||
column: 28
|
||||
@@ -46,9 +46,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- background
|
||||
- false
|
||||
- false
|
||||
name: background
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 16
|
||||
column: 7
|
||||
@@ -67,9 +67,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- datastructures
|
||||
- false
|
||||
- false
|
||||
name: datastructures
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 19
|
||||
column: 7
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- typing.Union
|
||||
- false
|
||||
- false
|
||||
name: typing.Union
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 30
|
||||
column: 4
|
||||
@@ -29,9 +29,9 @@ expression: diagnostics
|
||||
column: 0
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- typing.Awaitable
|
||||
- false
|
||||
- true
|
||||
name: typing.Awaitable
|
||||
ignore_init: false
|
||||
multiple: true
|
||||
location:
|
||||
row: 66
|
||||
column: 19
|
||||
@@ -50,9 +50,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- typing.AwaitableGenerator
|
||||
- false
|
||||
- true
|
||||
name: typing.AwaitableGenerator
|
||||
ignore_init: false
|
||||
multiple: true
|
||||
location:
|
||||
row: 66
|
||||
column: 30
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/rules/pyflakes/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- models.Nut
|
||||
- false
|
||||
- false
|
||||
name: models.Nut
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo1
|
||||
- false
|
||||
- false
|
||||
name: foo1
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 3
|
||||
column: 11
|
||||
@@ -25,9 +25,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo2
|
||||
- false
|
||||
- false
|
||||
name: foo2
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 4
|
||||
column: 11
|
||||
@@ -46,9 +46,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo3
|
||||
- false
|
||||
- false
|
||||
name: foo3
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 7
|
||||
column: 11
|
||||
@@ -67,9 +67,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo4
|
||||
- false
|
||||
- false
|
||||
name: foo4
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 11
|
||||
column: 11
|
||||
@@ -88,9 +88,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo5
|
||||
- false
|
||||
- false
|
||||
name: foo5
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 16
|
||||
column: 18
|
||||
@@ -109,9 +109,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo6
|
||||
- false
|
||||
- false
|
||||
name: foo6
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 21
|
||||
column: 16
|
||||
@@ -130,9 +130,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo7
|
||||
- false
|
||||
- false
|
||||
name: foo7
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 26
|
||||
column: 17
|
||||
@@ -151,9 +151,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo8
|
||||
- false
|
||||
- false
|
||||
name: foo8
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 30
|
||||
column: 18
|
||||
@@ -172,9 +172,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo9
|
||||
- false
|
||||
- false
|
||||
name: foo9
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 31
|
||||
column: 22
|
||||
@@ -193,9 +193,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo10
|
||||
- false
|
||||
- false
|
||||
name: foo10
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 35
|
||||
column: 15
|
||||
@@ -214,9 +214,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo11
|
||||
- false
|
||||
- false
|
||||
name: foo11
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 40
|
||||
column: 16
|
||||
@@ -235,9 +235,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo12
|
||||
- false
|
||||
- false
|
||||
name: foo12
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 46
|
||||
column: 7
|
||||
@@ -256,9 +256,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- foo13
|
||||
- false
|
||||
- false
|
||||
name: foo13
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 51
|
||||
column: 7
|
||||
|
||||
@@ -34,6 +34,8 @@ mod tests {
|
||||
#[test_case(Rule::MagicValueComparison, Path::new("magic_value_comparison.py"); "PLR2004")]
|
||||
#[test_case(Rule::UselessElseOnLoop, Path::new("useless_else_on_loop.py"); "PLW0120")]
|
||||
#[test_case(Rule::GlobalVariableNotAssigned, Path::new("global_variable_not_assigned.py"); "PLW0602")]
|
||||
#[test_case(Rule::InvalidAllFormat, Path::new("PLE0605.py"); "PLE0605")]
|
||||
#[test_case(Rule::InvalidAllObject, Path::new("PLE0604.py"); "PLE0604")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
25
src/rules/pylint/rules/invalid_all_format.rs
Normal file
25
src/rules/pylint/rules/invalid_all_format.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct InvalidAllFormat;
|
||||
);
|
||||
impl Violation for InvalidAllFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Invalid format for `__all__`, must be `tuple` or `list`")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLE0605
|
||||
pub fn invalid_all_format(checker: &mut Checker, expr: &Expr) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(InvalidAllFormat, Range::from_located(expr)));
|
||||
}
|
||||
25
src/rules/pylint/rules/invalid_all_object.rs
Normal file
25
src/rules/pylint/rules/invalid_all_object.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violation::Violation;
|
||||
|
||||
define_violation!(
|
||||
pub struct InvalidAllObject;
|
||||
);
|
||||
impl Violation for InvalidAllObject {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Invalid object in `__all__`, must contain only strings")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLE0604
|
||||
pub fn invalid_all_object(checker: &mut Checker, expr: &Expr) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(InvalidAllObject, Range::from_located(expr)));
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
pub use await_outside_async::await_outside_async;
|
||||
pub use constant_comparison::constant_comparison;
|
||||
pub use invalid_all_format::{invalid_all_format, InvalidAllFormat};
|
||||
pub use invalid_all_object::{invalid_all_object, InvalidAllObject};
|
||||
pub use magic_value_comparison::magic_value_comparison;
|
||||
pub use merge_isinstance::merge_isinstance;
|
||||
pub use property_with_parameters::property_with_parameters;
|
||||
@@ -12,6 +14,8 @@ pub use useless_import_alias::useless_import_alias;
|
||||
|
||||
mod await_outside_async;
|
||||
mod constant_comparison;
|
||||
mod invalid_all_format;
|
||||
mod invalid_all_object;
|
||||
mod magic_value_comparison;
|
||||
mod merge_isinstance;
|
||||
mod property_with_parameters;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/rules/pylint/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
InvalidAllObject: ~
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 7
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
source: src/rules/pylint/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
InvalidAllFormat: ~
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 7
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
InvalidAllFormat: ~
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 7
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
InvalidAllFormat: ~
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 7
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
InvalidAllFormat: ~
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 7
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -3,7 +3,7 @@ source: src/rules/pylint/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
GlobalVariableNotAssigned: x
|
||||
GlobalVariableNotAssigned: X
|
||||
location:
|
||||
row: 5
|
||||
column: 11
|
||||
@@ -13,7 +13,7 @@ expression: diagnostics
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
GlobalVariableNotAssigned: x
|
||||
GlobalVariableNotAssigned: X
|
||||
location:
|
||||
row: 9
|
||||
column: 11
|
||||
|
||||
@@ -238,9 +238,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- shelve
|
||||
- false
|
||||
- false
|
||||
name: shelve
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 85
|
||||
column: 7
|
||||
|
||||
@@ -4,9 +4,9 @@ expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- typing.Union
|
||||
- false
|
||||
- false
|
||||
name: typing.Union
|
||||
ignore_init: false
|
||||
multiple: false
|
||||
location:
|
||||
row: 37
|
||||
column: 8
|
||||
@@ -117,9 +117,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- typing.Awaitable
|
||||
- false
|
||||
- true
|
||||
name: typing.Awaitable
|
||||
ignore_init: false
|
||||
multiple: true
|
||||
location:
|
||||
row: 89
|
||||
column: 23
|
||||
@@ -138,9 +138,9 @@ expression: diagnostics
|
||||
parent: ~
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- typing.AwaitableGenerator
|
||||
- false
|
||||
- true
|
||||
name: typing.AwaitableGenerator
|
||||
ignore_init: false
|
||||
multiple: true
|
||||
location:
|
||||
row: 89
|
||||
column: 34
|
||||
|
||||
@@ -49,7 +49,7 @@ expression: diagnostics
|
||||
column: 8
|
||||
end_location:
|
||||
row: 35
|
||||
column: 49
|
||||
column: 41
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
@@ -59,6 +59,26 @@ expression: diagnostics
|
||||
column: 12
|
||||
end_location:
|
||||
row: 38
|
||||
column: 45
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
ErrorInsteadOfException: ~
|
||||
location:
|
||||
row: 45
|
||||
column: 8
|
||||
end_location:
|
||||
row: 45
|
||||
column: 49
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
ErrorInsteadOfException: ~
|
||||
location:
|
||||
row: 48
|
||||
column: 12
|
||||
end_location:
|
||||
row: 48
|
||||
column: 53
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -62,10 +62,14 @@ impl Violation for SyntaxError {
|
||||
// pyflakes
|
||||
|
||||
define_violation!(
|
||||
pub struct UnusedImport(pub String, pub bool, pub bool);
|
||||
pub struct UnusedImport {
|
||||
pub name: String,
|
||||
pub ignore_init: bool,
|
||||
pub multiple: bool,
|
||||
}
|
||||
);
|
||||
fn fmt_unused_import_autofix_msg(unused_import: &UnusedImport) -> String {
|
||||
let UnusedImport(name, _, multiple) = unused_import;
|
||||
let UnusedImport { name, multiple, .. } = unused_import;
|
||||
if *multiple {
|
||||
"Remove unused import".to_string()
|
||||
} else {
|
||||
@@ -77,7 +81,9 @@ impl Violation for UnusedImport {
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedImport(name, ignore_init, ..) = self;
|
||||
let UnusedImport {
|
||||
name, ignore_init, ..
|
||||
} = self;
|
||||
if *ignore_init {
|
||||
format!(
|
||||
"`{name}` imported but unused; consider adding to `__all__` or using a redundant \
|
||||
@@ -89,7 +95,7 @@ impl Violation for UnusedImport {
|
||||
}
|
||||
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
let UnusedImport(_, ignore_init, _) = self;
|
||||
let UnusedImport { ignore_init, .. } = self;
|
||||
if *ignore_init {
|
||||
None
|
||||
} else {
|
||||
@@ -174,7 +180,7 @@ impl Violation for PercentFormatInvalidFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatInvalidFormat(message) = self;
|
||||
format!("'...' % ... has invalid format string: {message}")
|
||||
format!("`%`-format string has invalid format string: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +190,7 @@ define_violation!(
|
||||
impl Violation for PercentFormatExpectedMapping {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("'...' % ... expected mapping but got sequence")
|
||||
format!("`%`-format string expected mapping but got sequence")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +200,7 @@ define_violation!(
|
||||
impl Violation for PercentFormatExpectedSequence {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("'...' % ... expected sequence but got mapping")
|
||||
format!("`%`-format string expected sequence but got mapping")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +212,7 @@ impl AlwaysAutofixableViolation for PercentFormatExtraNamedArguments {
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatExtraNamedArguments(missing) = self;
|
||||
let message = missing.join(", ");
|
||||
format!("'...' % ... has unused named argument(s): {message}")
|
||||
format!("`%`-format string has unused named argument(s): {message}")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
@@ -224,7 +230,7 @@ impl Violation for PercentFormatMissingArgument {
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatMissingArgument(missing) = self;
|
||||
let message = missing.join(", ");
|
||||
format!("'...' % ... is missing argument(s) for placeholder(s): {message}")
|
||||
format!("`%`-format string is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +240,7 @@ define_violation!(
|
||||
impl Violation for PercentFormatMixedPositionalAndNamed {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("'...' % ... has mixed positional and named placeholders")
|
||||
format!("`%`-format string has mixed positional and named placeholders")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +251,7 @@ impl Violation for PercentFormatPositionalCountMismatch {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatPositionalCountMismatch(wanted, got) = self;
|
||||
format!("'...' % ... has {wanted} placeholder(s) but {got} substitution(s)")
|
||||
format!("`%`-format string has {wanted} placeholder(s) but {got} substitution(s)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +261,7 @@ define_violation!(
|
||||
impl Violation for PercentFormatStarRequiresSequence {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("'...' % ... `*` specifier requires sequence")
|
||||
format!("`%`-format string `*` specifier requires sequence")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +272,7 @@ impl Violation for PercentFormatUnsupportedFormatCharacter {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let PercentFormatUnsupportedFormatCharacter(char) = self;
|
||||
format!("'...' % ... has unsupported format character '{char}'")
|
||||
format!("`%`-format string has unsupported format character '{char}'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +283,7 @@ impl Violation for StringDotFormatInvalidFormat {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatInvalidFormat(message) = self;
|
||||
format!("'...'.format(...) has invalid format string: {message}")
|
||||
format!("`.format` call has invalid format string: {message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +295,7 @@ impl AlwaysAutofixableViolation for StringDotFormatExtraNamedArguments {
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatExtraNamedArguments(missing) = self;
|
||||
let message = missing.join(", ");
|
||||
format!("'...'.format(...) has unused named argument(s): {message}")
|
||||
format!("`.format` call has unused named argument(s): {message}")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
@@ -307,7 +313,7 @@ impl Violation for StringDotFormatExtraPositionalArguments {
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatExtraPositionalArguments(missing) = self;
|
||||
let message = missing.join(", ");
|
||||
format!("'...'.format(...) has unused arguments at position(s): {message}")
|
||||
format!("`.format` call has unused arguments at position(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +325,7 @@ impl Violation for StringDotFormatMissingArguments {
|
||||
fn message(&self) -> String {
|
||||
let StringDotFormatMissingArguments(missing) = self;
|
||||
let message = missing.join(", ");
|
||||
format!("'...'.format(...) is missing argument(s) for placeholder(s): {message}")
|
||||
format!("`.format` call is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +335,7 @@ define_violation!(
|
||||
impl Violation for StringDotFormatMixingAutomatic {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("'...'.format(...) mixes automatic and manual numbering")
|
||||
format!("`.format` string mixes automatic and manual numbering")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,16 +817,17 @@ impl Violation for ConsiderMergingIsinstance {
|
||||
define_violation!(
|
||||
pub struct UseSysExit(pub String);
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UseSysExit {
|
||||
impl Violation for UseSysExit {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UseSysExit(name) = self;
|
||||
format!("Use `sys.exit()` instead of `{name}`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let UseSysExit(name) = self;
|
||||
format!("Replace `{name}` with `sys.exit()`")
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
Some(|UseSysExit(name)| format!("Replace `{name}` with `sys.exit()`"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -964,6 +971,8 @@ define_violation!(
|
||||
}
|
||||
);
|
||||
impl Violation for UnusedLoopControlVariable {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnusedLoopControlVariable { name, safe } = self;
|
||||
@@ -1056,7 +1065,7 @@ impl Violation for JumpStatementInFinally {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let JumpStatementInFinally(name) = self;
|
||||
format!("`{name}` inside finally blocks cause exceptions to be silenced")
|
||||
format!("`{name}` inside `finally` blocks cause exceptions to be silenced")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2168,16 +2177,17 @@ impl Violation for ReturnInTryExceptFinally {
|
||||
define_violation!(
|
||||
pub struct UseTernaryOperator(pub String);
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UseTernaryOperator {
|
||||
impl Violation for UseTernaryOperator {
|
||||
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UseTernaryOperator(contents) = self;
|
||||
format!("Use ternary operator `{contents}` instead of if-else-block")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let UseTernaryOperator(contents) = self;
|
||||
format!("Replace if-else-block with `{contents}`")
|
||||
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||
Some(|UseTernaryOperator(contents)| format!("Replace if-else-block with `{contents}`"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4055,7 +4065,10 @@ define_violation!(
|
||||
impl Violation for CallDatetimeToday {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("The use of `datetime.datetime.today()` is not allowed")
|
||||
format!(
|
||||
"The use of `datetime.datetime.today()` is not allowed, use \
|
||||
`datetime.datetime.now(tz=)` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4065,7 +4078,10 @@ define_violation!(
|
||||
impl Violation for CallDatetimeUtcnow {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("The use of `datetime.datetime.utcnow()` is not allowed")
|
||||
format!(
|
||||
"The use of `datetime.datetime.utcnow()` is not allowed, use \
|
||||
`datetime.datetime.now(tz=)` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4075,7 +4091,10 @@ define_violation!(
|
||||
impl Violation for CallDatetimeUtcfromtimestamp {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("The use of `datetime.datetime.utcfromtimestamp()` is not allowed")
|
||||
format!(
|
||||
"The use of `datetime.datetime.utcfromtimestamp()` is not allowed, use \
|
||||
`datetime.datetime.fromtimestamp(ts, tz=)` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4120,7 +4139,10 @@ define_violation!(
|
||||
impl Violation for CallDateToday {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("The use of `datetime.date.today()` is not allowed.")
|
||||
format!(
|
||||
"The use of `datetime.date.today()` is not allowed, use \
|
||||
`datetime.datetime.now(tz=).date()` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4130,7 +4152,10 @@ define_violation!(
|
||||
impl Violation for CallDateFromtimestamp {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("The use of `datetime.date.fromtimestamp()` is not allowed")
|
||||
format!(
|
||||
"The use of `datetime.date.fromtimestamp()` is not allowed, use \
|
||||
`datetime.datetime.fromtimestamp(ts, tz=).date()` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user