Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
651f6b6bce | ||
|
|
d3c3198b24 | ||
|
|
ec6054edce | ||
|
|
a0df78cb7d | ||
|
|
ac41c33d1f | ||
|
|
b4b8782243 | ||
|
|
8e53a4d1d3 | ||
|
|
668860cba3 | ||
|
|
038e8cfba0 | ||
|
|
ebfa55cea3 | ||
|
|
aa0fc0f9c2 | ||
|
|
aa85c81280 | ||
|
|
f5fd6f59ea | ||
|
|
540e31f5f4 | ||
|
|
8136cc9238 | ||
|
|
2c71535016 | ||
|
|
cce8fb9882 | ||
|
|
9e59c99133 | ||
|
|
b032f50775 | ||
|
|
1c2fc38853 | ||
|
|
f16f3a4a03 | ||
|
|
30a09ec211 | ||
|
|
ec7b25290b | ||
|
|
68422d4ff2 | ||
|
|
2abaffd65b | ||
|
|
06cbf5a2ae | ||
|
|
f432ce291a | ||
|
|
1eb331143d | ||
|
|
db1b1672b8 | ||
|
|
6861e59103 | ||
|
|
778c644ee3 | ||
|
|
e66a6b6d05 | ||
|
|
faea478ca5 | ||
|
|
39b5fa0e24 | ||
|
|
df413d1ece | ||
|
|
cfd0693ae5 | ||
|
|
56ad160c05 | ||
|
|
9d8c6ba671 | ||
|
|
1ea88ea56b | ||
|
|
7f44ffb55c | ||
|
|
dbd640d90f | ||
|
|
e5082c7d6c | ||
|
|
841d176289 | ||
|
|
c15595325c | ||
|
|
e97b1a4280 | ||
|
|
82ec884a61 | ||
|
|
7c1a6bce7b | ||
|
|
84a8b628b8 | ||
|
|
142b627bb8 | ||
|
|
fbf231e1b8 | ||
|
|
1dd9ccf7f6 | ||
|
|
d601abe01b | ||
|
|
15d4774b6b | ||
|
|
293c7e00d5 | ||
|
|
c3a3195922 | ||
|
|
39d98d3488 | ||
|
|
cd3d82213a | ||
|
|
a9a0026f2f | ||
|
|
da4618d77b | ||
|
|
1b0748d19d | ||
|
|
0b7fa64481 | ||
|
|
09d593b124 | ||
|
|
adc134ced0 | ||
|
|
6051a0c1c8 | ||
|
|
00495e8620 | ||
|
|
ad8693e3de | ||
|
|
69e20c4554 | ||
|
|
b5816634b3 | ||
|
|
e8810eae64 | ||
|
|
ba26a60e2a | ||
|
|
42459c35b0 | ||
|
|
1cbd929a0a | ||
|
|
5f07e70762 | ||
|
|
8963a62ec0 | ||
|
|
4589daa0bd | ||
|
|
ea0274d22c | ||
|
|
ca1129ad27 | ||
|
|
104c63afc6 | ||
|
|
ba457c21b5 | ||
|
|
a92958f941 | ||
|
|
1cd206285e | ||
|
|
5ac5b69e9f | ||
|
|
01fedec1e7 | ||
|
|
ef20692149 | ||
|
|
50046fbed3 | ||
|
|
6798675db1 | ||
|
|
8e5a944ce1 | ||
|
|
1e325edfb1 | ||
|
|
502574797f | ||
|
|
7a83b65fbe | ||
|
|
74e3cdfd7c | ||
|
|
2ef28f217c | ||
|
|
63fc912ed8 | ||
|
|
d76a47d366 | ||
|
|
b532fce792 | ||
|
|
3ee6a90905 | ||
|
|
0a6d2294a7 | ||
|
|
e66fb42d0b | ||
|
|
5165b703d9 | ||
|
|
b40cd1fabc | ||
|
|
64fb0bd2cc | ||
|
|
2ad29089af | ||
|
|
f41796d559 | ||
|
|
945a9e187c | ||
|
|
546413defb | ||
|
|
e371ef9b1a | ||
|
|
c9585fe304 | ||
|
|
535868f939 | ||
|
|
cec993aaa9 | ||
|
|
1a32d873f0 | ||
|
|
f308f9f27e | ||
|
|
73dccce5f5 | ||
|
|
fc9fae6579 | ||
|
|
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 |
19
.github/release.yml
vendored
Normal file
19
.github/release.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
|
||||
changelog:
|
||||
categories:
|
||||
- title: Breaking Changes
|
||||
labels:
|
||||
- breaking
|
||||
- title: Rules
|
||||
labels:
|
||||
- rule
|
||||
- autofix
|
||||
- title: Settings
|
||||
labels:
|
||||
- configuration
|
||||
- title: Bug Fixes
|
||||
labels:
|
||||
- bug
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- "*"
|
||||
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"}'
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,6 +1,9 @@
|
||||
# Local cache
|
||||
.ruff_cache
|
||||
resources/test/cpython
|
||||
docs/
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
@@ -182,3 +185,6 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
.vimspector.json
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.236
|
||||
rev: v0.0.240
|
||||
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,81 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.238
|
||||
|
||||
### `select`, `extend-select`, `ignore`, and `extend-ignore` have new semantics ([#2312](https://github.com/charliermarsh/ruff/pull/2312))
|
||||
|
||||
Previously, the interplay between `select` and its related options could lead to unexpected
|
||||
behavior. For example, `ruff --select E501 --ignore ALL` and `ruff --select E501 --extend-ignore
|
||||
ALL` behaved differently. (See [#2312](https://github.com/charliermarsh/ruff/pull/2312) for more
|
||||
examples.)
|
||||
|
||||
When Ruff determines the enabled rule set, it has to reconcile `select` and `ignore` from a variety
|
||||
of sources, including the current `pyproject.toml`, any inherited `pyproject.toml` files, and the
|
||||
CLI.
|
||||
|
||||
The new semantics are such that Ruff uses the "highest-priority" `select` as the basis for the rule
|
||||
set, and then applies any `extend-select`, `ignore`, and `extend-ignore` adjustments. CLI options
|
||||
are given higher priority than `pyproject.toml` options, and the current `pyproject.toml` file is
|
||||
given higher priority than any inherited `pyproject.toml` files.
|
||||
|
||||
`extend-select` and `extend-ignore` are no longer given "top priority"; instead, they merely append
|
||||
to the `select` and `ignore` lists, as in Flake8.
|
||||
|
||||
This change is largely backwards compatible -- most users should experience no change in behavior.
|
||||
However, as an example of a breaking change, consider the following:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
ignore = ["F401"]
|
||||
```
|
||||
|
||||
Running `ruff --select F` would previously have enabled all `F` rules, apart from `F401`. Now, it
|
||||
will enable all `F` rules, including `F401`, as the command line's `--select` resets the resolution.
|
||||
|
||||
### `remove-six-compat` (`UP016`) has been removed ([#2332](https://github.com/charliermarsh/ruff/pull/2332))
|
||||
|
||||
The `remove-six-compat` rule has been removed. This rule was only useful for one-time Python 2-to-3
|
||||
upgrades.
|
||||
|
||||
## 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))
|
||||
|
||||
@@ -4,10 +4,14 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
|
||||
|
||||
## The basics
|
||||
|
||||
Ruff welcomes contributions in the form of Pull Requests. For small changes (e.g., bug fixes), feel
|
||||
free to submit a PR. For larger changes (e.g., new lint rules, new functionality, new configuration
|
||||
options), consider submitting an [Issue](https://github.com/charliermarsh/ruff/issues) outlining
|
||||
your proposed change.
|
||||
Ruff welcomes contributions in the form of Pull Requests.
|
||||
|
||||
For small changes (e.g., bug fixes), feel free to submit a PR.
|
||||
|
||||
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
|
||||
creating an [**issue**](https://github.com/charliermarsh/ruff/issues) outlining your proposed
|
||||
change. You can also join us on [**Discord**](https://discord.gg/Z8KbeK24) to discuss your idea with
|
||||
the community.
|
||||
|
||||
If you're looking for a place to start, we recommend implementing a new lint rule (see:
|
||||
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
|
||||
@@ -39,20 +43,31 @@ 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.
|
||||
|
||||
Note that many code changes also require updating the snapshot tests, which is done interactively
|
||||
after running `cargo test` like so:
|
||||
|
||||
```shell
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
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 +94,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 +102,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 +110,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 +136,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 +146,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.240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -1922,7 +1922,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
@@ -1977,7 +1977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2014,7 +2014,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -2035,7 +2035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
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.240", 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" }
|
||||
@@ -77,7 +77,7 @@ wasm-bindgen = { version = "0.2.83" }
|
||||
is_executable = "1.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.19.1", features = ["yaml", "redactions"] }
|
||||
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
|
||||
test-case = { version = "2.2.2" }
|
||||
wasm-bindgen-test = { version = "0.3.33" }
|
||||
|
||||
|
||||
30
LICENSE
30
LICENSE
@@ -1005,3 +1005,33 @@ are:
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
- flake8-raise, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jon Dufresne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-self, licensed as follows:
|
||||
"""
|
||||
Freely Distributable
|
||||
"""
|
||||
|
||||
2964
flake8_to_ruff/Cargo.lock
generated
2964
flake8_to_ruff/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
50
mkdocs.template.yml
Normal file
50
mkdocs.template.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
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
|
||||
custom_dir: .overrides
|
||||
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
|
||||
@@ -14,6 +14,7 @@
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
<script src="https://cdn.usefathom.com/script.js" data-site="XWUDIXNB" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
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):
|
||||
...
|
||||
|
||||
@@ -4,6 +4,7 @@ d = {}
|
||||
safe = "s3cr3t"
|
||||
password = True
|
||||
password = safe
|
||||
password = ""
|
||||
password is True
|
||||
password == 1
|
||||
d["safe"] = "s3cr3t"
|
||||
|
||||
@@ -7,6 +7,7 @@ string = "Hello World"
|
||||
# OK
|
||||
func("s3cr3t")
|
||||
func(1, password=string)
|
||||
func(1, password="")
|
||||
func(pos="s3cr3t", password=string)
|
||||
|
||||
# Error
|
||||
|
||||
@@ -28,3 +28,7 @@ def ok_all(first, /, pos, default="posonly", *, kwonly="kwonly"):
|
||||
|
||||
def default_all(first, /, pos, secret="posonly", *, password="kwonly"):
|
||||
pass
|
||||
|
||||
|
||||
def ok_empty(first, password=""):
|
||||
pass
|
||||
|
||||
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
|
||||
@@ -7,6 +7,13 @@ import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
import tensorflow # unconventional
|
||||
import holoviews # unconventional
|
||||
import panel # unconventional
|
||||
import plotly.express # unconventional
|
||||
import matplotlib # unconventional
|
||||
import polars # unconventional
|
||||
import pyarrow # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
@@ -15,6 +22,13 @@ import dask.dataframe as ddf # unconventional
|
||||
import numpy as nmp # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
import tensorflow as tfz # unconventional
|
||||
import holoviews as hsv # unconventional
|
||||
import panel as pns # unconventional
|
||||
import plotly.express as pltx # unconventional
|
||||
import matplotlib as ml # unconventional
|
||||
import polars as ps # unconventional
|
||||
import pyarrow as arr # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import dask.array as da # conventional
|
||||
@@ -23,3 +37,12 @@ import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
import tensorflow as tf # conventional
|
||||
import holoviews as hv # conventional
|
||||
import panel as pn # conventional
|
||||
import plotly.express as px # conventional
|
||||
import matplotlib as mpl # conventional
|
||||
import polars as pl # conventional
|
||||
import pyarrow as pa # conventional
|
||||
|
||||
from tensorflow.keras import Model # conventional
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
logging.error('Hello World', exc_info=True)
|
||||
# G201
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
logging.error("Hello World", exc_info=True)
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
logging.error("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
# OK
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
logging.error("Hello World", exc_info=False)
|
||||
|
||||
logging.error("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
logging.exception('Hello World', exc_info=True)
|
||||
# G202
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
logging.exception("Hello World", exc_info=True)
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
logging.exception("Hello World", exc_info=sys.exc_info())
|
||||
|
||||
# OK
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
logging.exception("Hello World", exc_info=False)
|
||||
|
||||
logging.exception("Hello World", exc_info=True)
|
||||
|
||||
0
resources/test/fixtures/flake8_no_pep420/test_pass_pyi/example.pyi
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass_pyi/example.pyi
vendored
Normal file
@@ -14,3 +14,60 @@ def ok_other_scope():
|
||||
@pytest.fixture(scope="function")
|
||||
def error():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", name="my_fixture")
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(name="my_fixture", scope="function")
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(name="my_fixture", scope="function", **kwargs)
|
||||
def error_second_arg():
|
||||
...
|
||||
|
||||
|
||||
# pytest.fixture does not take positional arguments, however this
|
||||
# tests the general case as we use a helper function that should
|
||||
# work for all cases.
|
||||
@pytest.fixture("my_fixture", scope="function")
|
||||
def error_arg():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="function",
|
||||
name="my_fixture",
|
||||
)
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
name="my_fixture",
|
||||
scope="function",
|
||||
)
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
"hello",
|
||||
name,
|
||||
*args
|
||||
,
|
||||
|
||||
# another comment ,)
|
||||
|
||||
scope=\
|
||||
"function" # some comment ),
|
||||
,
|
||||
|
||||
name2=name, name3="my_fixture", **kwargs
|
||||
)
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
this_should_be_linted = "double quote string"
|
||||
this_should_be_linted = u"double quote string"
|
||||
this_should_be_linted = f"double quote string"
|
||||
this_should_be_linted = f"double {'quote'} string"
|
||||
|
||||
@@ -4,3 +4,8 @@ this_is_fine = '"This" is a \'string\''
|
||||
this_is_fine = "This is a 'string'"
|
||||
this_is_fine = "\"This\" is a 'string'"
|
||||
this_is_fine = r'This is a \'string\''
|
||||
this_is_fine = R'This is a \'string\''
|
||||
this_should_raise = (
|
||||
'This is a'
|
||||
'\'string\''
|
||||
)
|
||||
|
||||
27
resources/test/fixtures/flake8_quotes/doubles_implicit.py
vendored
Normal file
27
resources/test/fixtures/flake8_quotes/doubles_implicit.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
x = (
|
||||
"This"
|
||||
"is"
|
||||
"not"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This" \
|
||||
"is" \
|
||||
"not"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This"
|
||||
"is 'actually'"
|
||||
"fine"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This" \
|
||||
"is 'actually'" \
|
||||
"fine"
|
||||
)
|
||||
|
||||
if True:
|
||||
"This can use 'double' quotes"
|
||||
"But this needs to be changed"
|
||||
@@ -1,2 +1,4 @@
|
||||
this_should_be_linted = 'single quote string'
|
||||
this_should_be_linted = u'double quote string'
|
||||
this_should_be_linted = f'double quote string'
|
||||
this_should_be_linted = f'double {"quote"} string'
|
||||
|
||||
@@ -3,3 +3,8 @@ this_is_fine = "'This' is a \"string\""
|
||||
this_is_fine = 'This is a "string"'
|
||||
this_is_fine = '\'This\' is a "string"'
|
||||
this_is_fine = r"This is a \"string\""
|
||||
this_is_fine = R"This is a \"string\""
|
||||
this_should_raise = (
|
||||
"This is a"
|
||||
"\"string\""
|
||||
)
|
||||
|
||||
27
resources/test/fixtures/flake8_quotes/singles_implicit.py
vendored
Normal file
27
resources/test/fixtures/flake8_quotes/singles_implicit.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
x = (
|
||||
'This'
|
||||
'is'
|
||||
'not'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This' \
|
||||
'is' \
|
||||
'not'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This'
|
||||
'is "actually"'
|
||||
'fine'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This' \
|
||||
'is "actually"' \
|
||||
'fine'
|
||||
)
|
||||
|
||||
if True:
|
||||
'This can use "single" quotes'
|
||||
'But this needs to be changed'
|
||||
15
resources/test/fixtures/flake8_raise/RSE102.py
vendored
Normal file
15
resources/test/fixtures/flake8_raise/RSE102.py
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
try:
|
||||
y = 6 + "7"
|
||||
except TypeError:
|
||||
raise ValueError() # RSE102
|
||||
|
||||
try:
|
||||
x = 1 / 0
|
||||
except ZeroDivisionError:
|
||||
raise
|
||||
|
||||
raise TypeError() # RSE102
|
||||
|
||||
raise AssertionError
|
||||
|
||||
raise AttributeError("test message")
|
||||
31
resources/test/fixtures/flake8_self/SLF001.py
vendored
Normal file
31
resources/test/fixtures/flake8_self/SLF001.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
class Foo:
|
||||
|
||||
def __init__(self):
|
||||
self.public_thing = "foo"
|
||||
self._private_thing = "bar"
|
||||
self.__really_private_thing = "baz"
|
||||
|
||||
def __str__(self):
|
||||
return "foo"
|
||||
|
||||
def public_func(self):
|
||||
pass
|
||||
|
||||
def _private_func(self):
|
||||
pass
|
||||
|
||||
def __really_private_func(self, arg):
|
||||
pass
|
||||
|
||||
|
||||
foo = Foo()
|
||||
|
||||
print(foo.public_thing)
|
||||
print(foo.public_func())
|
||||
print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
|
||||
print(foo._private_thing) # SLF001
|
||||
print(foo.__really_private_thing) # SLF001
|
||||
print(foo._private_func()) # SLF001
|
||||
print(foo.__really_private_func(1)) # SLF001
|
||||
@@ -21,3 +21,10 @@ if isinstance(a, int) and isinstance(b, bool) or isinstance(a, float):
|
||||
|
||||
if isinstance(a, bool) or isinstance(b, str):
|
||||
pass
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def isinstance(a, b):
|
||||
return False
|
||||
if isinstance(a, int) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
@@ -6,6 +6,14 @@ def f():
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a:
|
||||
@@ -50,3 +58,29 @@ def f():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
return False
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -54,7 +54,7 @@ else:
|
||||
randbytes = _get_random_bytes
|
||||
|
||||
|
||||
# OK (includes comments)
|
||||
# SIM108 (without fix due to comments)
|
||||
if x > 0:
|
||||
# test test
|
||||
abc = x
|
||||
@@ -91,3 +91,22 @@ if True:
|
||||
b = cccccccccccccccccccccccccccccccccccc
|
||||
else:
|
||||
b = ddddddddddddddddddddddddddddddddddddd
|
||||
|
||||
|
||||
# SIM108 (without fix due to trailing comment)
|
||||
if True:
|
||||
exitcode = 0
|
||||
else:
|
||||
exitcode = 1 # Trailing comment
|
||||
|
||||
|
||||
# SIM108
|
||||
if True: x = 3 # Foo
|
||||
else: x = 5
|
||||
|
||||
|
||||
# SIM108
|
||||
if True: # Foo
|
||||
x = 3
|
||||
else:
|
||||
x = 5
|
||||
|
||||
@@ -115,3 +115,43 @@ def f():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def any(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def all(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -115,3 +115,43 @@ def f():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def any(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def all(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -5,3 +5,10 @@ a = True if b != c else False # SIM210
|
||||
a = True if b + c else False # SIM210
|
||||
|
||||
a = False if b else True # OK
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
return False
|
||||
|
||||
a = True if b else False
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
# 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
|
||||
0 < (number - 100) # 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
|
||||
(number - 100) > 0
|
||||
|
||||
@@ -4,15 +4,22 @@ if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
|
||||
|
||||
if False:
|
||||
pass # TCH005
|
||||
|
||||
if 0:
|
||||
pass # TCH005
|
||||
|
||||
|
||||
def example():
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
pass # TCH005
|
||||
return
|
||||
|
||||
|
||||
class Test:
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
pass # TCH005
|
||||
x = 2
|
||||
|
||||
|
||||
@@ -23,3 +30,10 @@ if TYPE_CHECKING:
|
||||
|
||||
if TYPE_CHECKING:
|
||||
x: List
|
||||
|
||||
|
||||
if False:
|
||||
x: List
|
||||
|
||||
if 0:
|
||||
x: List
|
||||
|
||||
@@ -29,3 +29,4 @@ os.path.splitext(p)
|
||||
with open(p) as fp:
|
||||
fp.read()
|
||||
open(p).close()
|
||||
os.getcwdb(p)
|
||||
|
||||
8
resources/test/fixtures/isort/forced_separate.py
vendored
Normal file
8
resources/test/fixtures/isort/forced_separate.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# office_helper and tests are both first-party,
|
||||
# but we want tests and experiments to be separated, in that order
|
||||
from office_helper.core import CoreState
|
||||
import tests.common.foo as tcf
|
||||
from tests.common import async_mock_service
|
||||
from experiments.starry import *
|
||||
from experiments.weird import varieties
|
||||
from office_helper.assistants import entity_registry as er
|
||||
13
resources/test/fixtures/isort/lines_after_imports_class_after.py
vendored
Normal file
13
resources/test/fixtures/isort/lines_after_imports_class_after.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
class Thing(object):
|
||||
name: str
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
22
resources/test/fixtures/isort/lines_after_imports_func_after.py
vendored
Normal file
22
resources/test/fixtures/isort/lines_after_imports_func_after.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
my_local_folder_object.get()
|
||||
9
resources/test/fixtures/isort/lines_after_imports_nothing_after.py
vendored
Normal file
9
resources/test/fixtures/isort/lines_after_imports_nothing_after.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
29
resources/test/fixtures/isort/preserve_tabs.py
vendored
Normal file
29
resources/test/fixtures/isort/preserve_tabs.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
from numpy import (
|
||||
cos,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
sin,
|
||||
tan,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
)
|
||||
|
||||
if True:
|
||||
# inside nested block
|
||||
from numpy import (
|
||||
cos,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
sin,
|
||||
tan,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
)
|
||||
13
resources/test/fixtures/isort/preserve_tabs_2.py
vendored
Normal file
13
resources/test/fixtures/isort/preserve_tabs_2.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from numpy import (
|
||||
cos,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
sin,
|
||||
tan,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
)
|
||||
3
resources/test/fixtures/isort/pyproject.toml
vendored
3
resources/test/fixtures/isort/pyproject.toml
vendored
@@ -1,2 +1,5 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.isort]
|
||||
lines-after-imports = 3
|
||||
|
||||
3
resources/test/fixtures/isort/star_before_others.py
vendored
Normal file
3
resources/test/fixtures/isort/star_before_others.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
from .logging import config_logging
|
||||
from .settings import ENV
|
||||
from .settings import *
|
||||
20
resources/test/fixtures/pandas_vet/PD002.py
vendored
Normal file
20
resources/test/fixtures/pandas_vet/PD002.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import pandas as pd
|
||||
|
||||
x = pd.DataFrame()
|
||||
|
||||
x.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x.drop(["a"], axis=1, inplace=True)
|
||||
|
||||
x.drop(
|
||||
inplace=True,
|
||||
columns=["a"],
|
||||
axis=1,
|
||||
)
|
||||
|
||||
if True:
|
||||
x.drop(
|
||||
inplace=True,
|
||||
columns=["a"],
|
||||
axis=1,
|
||||
)
|
||||
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,
|
||||
)
|
||||
8
resources/test/fixtures/pyflakes/F841_0.py
vendored
8
resources/test/fixtures/pyflakes/F841_0.py
vendored
@@ -86,3 +86,11 @@ def f():
|
||||
open("") as ((this, that)),
|
||||
):
|
||||
print("hello")
|
||||
|
||||
|
||||
def f():
|
||||
exponential, base_multiplier = 1, 2
|
||||
hash_map = {
|
||||
(exponential := (exponential * base_multiplier) % 3): i + 1 for i in range(2)
|
||||
}
|
||||
return hash_map
|
||||
|
||||
@@ -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
|
||||
|
||||
19
resources/test/fixtures/pylint/invalid_all_format.py
vendored
Normal file
19
resources/test/fixtures/pylint/invalid_all_format.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
__all__ = "CONST" # [invalid-all-format]
|
||||
|
||||
__all__ = ["Hello"] + {"world"} # [invalid-all-format]
|
||||
|
||||
__all__ += {"world"} # [invalid-all-format]
|
||||
|
||||
__all__ = {"world"} + ["Hello"] # [invalid-all-format]
|
||||
|
||||
__all__ = (x for x in ["Hello", "world"]) # [invalid-all-format]
|
||||
|
||||
__all__ = {x for x in ["Hello", "world"]} # [invalid-all-format]
|
||||
|
||||
__all__ = ["Hello"]
|
||||
|
||||
__all__ = ("Hello",)
|
||||
|
||||
__all__ = ["Hello"] + ("world",)
|
||||
|
||||
__all__ = [x for x in ["Hello", "world"]]
|
||||
11
resources/test/fixtures/pylint/invalid_all_object.py
vendored
Normal file
11
resources/test/fixtures/pylint/invalid_all_object.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
__all__ = (
|
||||
None, # [invalid-all-object]
|
||||
Fruit,
|
||||
Worm,
|
||||
)
|
||||
|
||||
class Fruit:
|
||||
pass
|
||||
|
||||
class Worm:
|
||||
pass
|
||||
@@ -47,7 +47,7 @@ if input_password == "": # correct
|
||||
if input_password == ADMIN_PASSWORD: # correct
|
||||
pass
|
||||
|
||||
if input_password == "Hunter2": # [magic-value-comparison]
|
||||
if input_password == "Hunter2": # correct
|
||||
pass
|
||||
|
||||
PI = 3.141592653589793238
|
||||
@@ -62,7 +62,7 @@ if pi_estimation == PI: # correct
|
||||
HELLO_WORLD = b"Hello, World!"
|
||||
user_input = b"Hello, There!"
|
||||
|
||||
if user_input == b"something": # [magic-value-comparison]
|
||||
if user_input == b"something": # correct
|
||||
pass
|
||||
|
||||
if user_input == HELLO_WORLD: # correct
|
||||
|
||||
34
resources/test/fixtures/pylint/too_many_args.py
vendored
Normal file
34
resources/test/fixtures/pylint/too_many_args.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
def f(x, y, z, t, u, v, w, r): # Too many arguments (8/5)
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, t, u): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, _t, _u, _v, _w, r): # OK (underscore-prefixed names are ignored
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, u=1, v=1, r=1): # Too many arguments (6/5)
|
||||
pass
|
||||
|
||||
|
||||
def f(x=1, y=1, z=1): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, /, u, v, w): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many arguments (6/5)
|
||||
pass
|
||||
10
resources/test/fixtures/pylint/too_many_args_params.py
vendored
Normal file
10
resources/test/fixtures/pylint/too_many_args_params.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Too many args (6/4) for max_args=4
|
||||
# OK for dummy_variable_rgx ~ "skip_.*"
|
||||
def f(x, y, z, skip_t, skip_u, skip_v):
|
||||
pass
|
||||
|
||||
|
||||
# Too many args (6/4) for max_args=4
|
||||
# Too many args (6/5) for dummy_variable_rgx ~ "skip_.*"
|
||||
def f(x, y, z, t, u, v):
|
||||
pass
|
||||
56
resources/test/fixtures/pylint/too_many_statements.py
vendored
Normal file
56
resources/test/fixtures/pylint/too_many_statements.py
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
def f(): # OK
|
||||
return
|
||||
|
||||
|
||||
async def f(): # Too many statements (52/50)
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
3
resources/test/fixtures/pylint/too_many_statements_params.py
vendored
Normal file
3
resources/test/fixtures/pylint/too_many_statements_params.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Too may statements (2/1) for max_statements=1
|
||||
def f(x):
|
||||
pass
|
||||
87
resources/test/fixtures/pyupgrade/UP016.py
vendored
87
resources/test/fixtures/pyupgrade/UP016.py
vendored
@@ -1,87 +0,0 @@
|
||||
# Replace names by built-in names, whether namespaced or not
|
||||
# https://github.com/search?q=%22from+six+import%22&type=code
|
||||
import six
|
||||
from six.moves import map # No need
|
||||
from six import text_type
|
||||
|
||||
six.text_type # str
|
||||
six.binary_type # bytes
|
||||
six.class_types # (type,)
|
||||
six.string_types # (str,)
|
||||
six.integer_types # (int,)
|
||||
six.unichr # chr
|
||||
six.iterbytes # iter
|
||||
six.print_(...) # print(...)
|
||||
six.exec_(c, g, l) # exec(c, g, l)
|
||||
six.advance_iterator(it) # next(it)
|
||||
six.next(it) # next(it)
|
||||
six.callable(x) # callable(x)
|
||||
six.moves.range(x) # range(x)
|
||||
six.moves.xrange(x) # range(x)
|
||||
isinstance(..., six.class_types) # isinstance(..., type)
|
||||
issubclass(..., six.integer_types) # issubclass(..., int)
|
||||
isinstance(..., six.string_types) # isinstance(..., str)
|
||||
|
||||
# Replace call on arg by method call on arg
|
||||
six.iteritems(dct) # dct.items()
|
||||
six.iterkeys(dct) # dct.keys()
|
||||
six.itervalues(dct) # dct.values()
|
||||
six.viewitems(dct) # dct.items()
|
||||
six.viewkeys(dct) # dct.keys()
|
||||
six.viewvalues(dct) # dct.values()
|
||||
six.assertCountEqual(self, a1, a2) # self.assertCountEqual(a1, a2)
|
||||
six.assertRaisesRegex(self, e, r, fn) # self.assertRaisesRegex(e, r, fn)
|
||||
six.assertRegex(self, s, r) # self.assertRegex(s, r)
|
||||
|
||||
# Replace call on arg by arg attribute
|
||||
six.get_method_function(meth) # meth.__func__
|
||||
six.get_method_self(meth) # meth.__self__
|
||||
six.get_function_closure(fn) # fn.__closure__
|
||||
six.get_function_code(fn) # fn.__code__
|
||||
six.get_function_defaults(fn) # fn.__defaults__
|
||||
six.get_function_globals(fn) # fn.__globals__
|
||||
|
||||
# Replace by string literal
|
||||
six.b("...") # b'...'
|
||||
six.u("...") # '...'
|
||||
six.ensure_binary("...") # b'...'
|
||||
six.ensure_str("...") # '...'
|
||||
six.ensure_text("...") # '...'
|
||||
six.b(string) # no change
|
||||
|
||||
# Replace by simple expression
|
||||
six.get_unbound_function(meth) # meth
|
||||
six.create_unbound_method(fn, cls) # fn
|
||||
|
||||
# Raise exception
|
||||
six.raise_from(exc, exc_from) # raise exc from exc_from
|
||||
six.reraise(tp, exc, tb) # raise exc.with_traceback(tb)
|
||||
six.reraise(*sys.exc_info()) # raise
|
||||
|
||||
# Int / Bytes conversion
|
||||
six.byte2int(bs) # bs[0]
|
||||
six.indexbytes(bs, i) # bs[i]
|
||||
six.int2byte(i) # bytes((i, ))
|
||||
|
||||
# Special cases for next calls
|
||||
next(six.iteritems(dct)) # next(iter(dct.items()))
|
||||
next(six.iterkeys(dct)) # next(iter(dct.keys()))
|
||||
next(six.itervalues(dct)) # next(iter(dct.values()))
|
||||
|
||||
# TODO: To implement
|
||||
|
||||
|
||||
# Rewrite classes
|
||||
@six.python_2_unicode_compatible # Remove
|
||||
class C(six.Iterator):
|
||||
pass # class C: pass
|
||||
|
||||
|
||||
class C(six.with_metaclass(M, B)):
|
||||
pass # class C(B, metaclass=M): pass
|
||||
|
||||
|
||||
# class C(B, metaclass=M): pass
|
||||
@six.add_metaclass(M)
|
||||
class C(B):
|
||||
pass
|
||||
50
resources/test/fixtures/pyupgrade/UP035.py
vendored
Normal file
50
resources/test/fixtures/pyupgrade/UP035.py
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# UP035
|
||||
from collections import Mapping
|
||||
|
||||
from collections import Mapping as MAP
|
||||
|
||||
from collections import Mapping, Sequence
|
||||
|
||||
from collections import Counter, Mapping
|
||||
|
||||
from collections import (Counter, Mapping)
|
||||
|
||||
from collections import (Counter,
|
||||
Mapping)
|
||||
|
||||
from collections import Counter, \
|
||||
Mapping
|
||||
|
||||
from collections import Counter, Mapping, Sequence
|
||||
|
||||
from collections import Mapping as mapping, Counter
|
||||
|
||||
if True:
|
||||
from collections import Mapping, Counter
|
||||
|
||||
if True:
|
||||
if True:
|
||||
pass
|
||||
from collections import Mapping, Counter
|
||||
|
||||
if True: from collections import Mapping
|
||||
|
||||
import os
|
||||
from collections import Counter, Mapping
|
||||
import sys
|
||||
|
||||
if True:
|
||||
from collections import (
|
||||
Mapping,
|
||||
Callable,
|
||||
Bad,
|
||||
Good,
|
||||
)
|
||||
|
||||
from typing import Callable, Match, Pattern, List
|
||||
|
||||
if True: from collections import (
|
||||
Mapping, Counter)
|
||||
|
||||
# OK
|
||||
from a import b
|
||||
180
resources/test/fixtures/pyupgrade/UP036_0.py
vendored
Normal file
180
resources/test/fixtures/pyupgrade/UP036_0.py
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
if True:
|
||||
print("py2!")
|
||||
else:
|
||||
print("???")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0): print("PY2!")
|
||||
else: print("PY3!")
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
print("PY2")
|
||||
else:
|
||||
print("PY3")
|
||||
|
||||
if sys.version_info < (3,0): print(1 if True else 3)
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f():
|
||||
print("py2")
|
||||
else:
|
||||
def f():
|
||||
print("py3")
|
||||
print("This the next")
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
|
||||
x = 1
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
# ohai
|
||||
|
||||
x = 1
|
||||
|
||||
if sys.version_info > (3,0): print("py3")
|
||||
else: print("py2")
|
||||
|
||||
if sys.version_info > (3,):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
if sys.version_info < (3,):
|
||||
print("py2")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
def f():
|
||||
if sys.version_info < (3,0):
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
pass
|
||||
else:
|
||||
yield
|
||||
|
||||
|
||||
class C:
|
||||
def g():
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f(py2):
|
||||
pass
|
||||
else:
|
||||
def f(py3):
|
||||
pass
|
||||
|
||||
def h():
|
||||
pass
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
# comment
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f():
|
||||
print("py2")
|
||||
def g():
|
||||
print("py2")
|
||||
else:
|
||||
def f():
|
||||
print("py3")
|
||||
def g():
|
||||
print("py3")
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print(3)
|
||||
# comment
|
||||
print(2+3)
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,): print(3)
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
|
||||
if True:
|
||||
if sys.version_info <= (3, 0):
|
||||
expected_error = []
|
||||
else:
|
||||
expected_error = [
|
||||
"<stdin>:1:5: Generator expression must be parenthesized",
|
||||
"max(1 for i in range(10), key=lambda x: x+1)",
|
||||
" ^",
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info <= (3, 0):
|
||||
expected_error = []
|
||||
else:
|
||||
expected_error = [
|
||||
"<stdin>:1:5: Generator expression must be parenthesized",
|
||||
"max(1 for i in range(10), key=lambda x: x+1)",
|
||||
" ^",
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
"""the indentation on
|
||||
this line is significant"""
|
||||
|
||||
"this is" \
|
||||
"allowed too"
|
||||
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
if sys.version_info > (3, 0): expected_error = \
|
||||
[]
|
||||
|
||||
if sys.version_info > (3, 0): expected_error = []
|
||||
|
||||
if sys.version_info > (3, 0): \
|
||||
expected_error = []
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): expected_error = \
|
||||
[]
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): expected_error = []
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): \
|
||||
expected_error = []
|
||||
76
resources/test/fixtures/pyupgrade/UP036_1.py
vendored
Normal file
76
resources/test/fixtures/pyupgrade/UP036_1.py
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info == 2:
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info < (3,):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info == 3:
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if sys.version_info > (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
from sys import version_info
|
||||
|
||||
if version_info > (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info < (3,0):
|
||||
print(2)
|
||||
else:
|
||||
print(3)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
def f():
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info < (3,0):
|
||||
print(2)
|
||||
else:
|
||||
print(3)
|
||||
|
||||
def f():
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
62
resources/test/fixtures/pyupgrade/UP036_2.py
vendored
Normal file
62
resources/test/fixtures/pyupgrade/UP036_2.py
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info > (3, 5):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if version_info > (3, 5):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if sys.version_info >= (3,6):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if version_info >= (3,6):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if sys.version_info < (3,6):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info <= (3,5):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info <= (3, 5):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
|
||||
if sys.version_info > (3,):
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
|
||||
if sys.version_info[0] > "2":
|
||||
3
|
||||
else:
|
||||
2
|
||||
24
resources/test/fixtures/pyupgrade/UP036_3.py
vendored
Normal file
24
resources/test/fixtures/pyupgrade/UP036_3.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
for item in range(10):
|
||||
print(f"PY2-{item}")
|
||||
else :
|
||||
print("py3")
|
||||
for item in range(10):
|
||||
print(f"PY3-{item}")
|
||||
|
||||
if False:
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
for item in range(10):
|
||||
print(f"PY2-{item}")
|
||||
else :
|
||||
print("py3")
|
||||
for item in range(10):
|
||||
print(f"PY3-{item}")
|
||||
|
||||
|
||||
if sys.version_info < (3,0): print("PY2!")
|
||||
else : print("PY3!")
|
||||
45
resources/test/fixtures/pyupgrade/UP036_4.py
vendored
Normal file
45
resources/test/fixtures/pyupgrade/UP036_4.py
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
import sys
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
|
||||
if True:
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if True:
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
elif foo:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
else:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
else:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
elif foo:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
2
resources/test/fixtures/ruff/RUF100_0.py
vendored
2
resources/test/fixtures/ruff/RUF100_0.py
vendored
@@ -86,3 +86,5 @@ import shelve # noqa: RUF100
|
||||
import sys # noqa: F401, RUF100
|
||||
|
||||
print(sys.path)
|
||||
|
||||
"shape: (6,)\nSeries: '' [duration[μs]]\n[\n\t0µs\n\t1µs\n\t2µs\n\t3µs\n\t4µs\n\t5µs\n]" # noqa: F401
|
||||
|
||||
1
resources/test/fixtures/ruff/RUF100_2.py
vendored
Normal file
1
resources/test/fixtures/ruff/RUF100_2.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import itertools # noqa: F401
|
||||
10
resources/test/fixtures/tryceratops/TRY201.py
vendored
10
resources/test/fixtures/tryceratops/TRY201.py
vendored
@@ -35,6 +35,16 @@ def still_good():
|
||||
raise
|
||||
|
||||
|
||||
def still_actually_good():
|
||||
try:
|
||||
process()
|
||||
except MyException as e:
|
||||
try:
|
||||
pass
|
||||
except TypeError:
|
||||
raise e
|
||||
|
||||
|
||||
def bad_that_needs_recursion():
|
||||
try:
|
||||
process()
|
||||
|
||||
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
|
||||
|
||||
@@ -66,18 +66,8 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"extend-ignore": {
|
||||
"description": "A list of rule codes or prefixes to ignore, in addition to those specified by `ignore`.\n\nNote that `extend-ignore` is applied after resolving rules from `ignore`/`select` and a less specific rule in `extend-ignore` would overwrite a more specific rule in `select`. It is recommended to only use `extend-ignore` when extending a `pyproject.toml` file via `extend`.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"extend-select": {
|
||||
"description": "A list of rule codes or prefixes to enable, in addition to those specified by `select`.\n\nNote that `extend-select` is applied after resolving rules from `ignore`/`select` and a less specific rule in `extend-select` would overwrite a more specific rule in `ignore`. It is recommended to only use `extend-select` when extending a `pyproject.toml` file via `extend`.",
|
||||
"description": "A list of rule codes or prefixes to enable, in addition to those specified by `select`.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
@@ -111,7 +101,7 @@
|
||||
]
|
||||
},
|
||||
"fixable": {
|
||||
"description": "A list of rule codes or prefixes to consider autofixable.",
|
||||
"description": "A list of rule codes or prefixes to consider autofixable. By default, all rules are considered autofixable.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
@@ -578,6 +568,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": [
|
||||
@@ -652,7 +649,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow-multiline": {
|
||||
"description": "Whether to allow implicit string concatenations for multiline strings. By default, implicit concatenations of multiline strings are allowed (but continuation lines, delimited with a backslash, are prohibited).",
|
||||
"description": "Whether to allow implicit string concatenations for multiline strings. By default, implicit concatenations of multiline strings are allowed (but continuation lines, delimited with a backslash, are prohibited).\n\nNote that setting `allow-multiline = false` should typically be coupled with disabling `explicit-string-concatenation` (`ISC003`). Otherwise, both explicit and implicit multiline string concatenations will be seen as violations.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -771,7 +768,7 @@
|
||||
]
|
||||
},
|
||||
"docstring-quotes": {
|
||||
"description": "Quote style to prefer for docstrings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for docstrings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -782,7 +779,7 @@
|
||||
]
|
||||
},
|
||||
"inline-quotes": {
|
||||
"description": "Quote style to prefer for inline strings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for inline strings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -793,7 +790,7 @@
|
||||
]
|
||||
},
|
||||
"multiline-quotes": {
|
||||
"description": "Quote style to prefer for multiline strings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for multiline strings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -940,6 +937,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forced-separate": {
|
||||
"description": "A list of modules to separate into auxiliary block(s) of imports, in the order specified.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"known-first-party": {
|
||||
"description": "A list of modules to consider first-party, regardless of whether they can be identified as such via introspection of the local filesystem.",
|
||||
"type": [
|
||||
@@ -960,6 +967,14 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"lines-after-imports": {
|
||||
"description": "The number of blank lines to place after imports. -1 for automatic determination.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int"
|
||||
},
|
||||
"no-lines-before": {
|
||||
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
|
||||
"type": [
|
||||
@@ -981,7 +996,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"
|
||||
@@ -1105,7 +1120,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keep-runtime-typing": {
|
||||
"description": "Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.",
|
||||
"description": "Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively, and enabling it is equivalent to disabling `use-pep585-annotation` (`UP006`) and `use-pep604-annotation` (`UP007`) entirely.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -1118,7 +1133,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ignore-overlong-task-comments": {
|
||||
"description": "Whether or not line-length violations (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"description": "Whether line-length violations (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -1157,7 +1172,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow-magic-value-types": {
|
||||
"description": "Constant types to ignore when used as \"magic values\".",
|
||||
"description": "Constant types to ignore when used as \"magic values\" (see: `PLR2004`).",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
@@ -1165,6 +1180,24 @@
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConstantType"
|
||||
}
|
||||
},
|
||||
"max-args": {
|
||||
"description": "Maximum number of arguments allowed for a function definition (see: `PLR0913`).",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max-statements": {
|
||||
"description": "Maximum number of statements allowed for a method or a statement (see: `PLR0915`).",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1182,14 +1215,14 @@
|
||||
"Quote": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Use single quotes (`'`).",
|
||||
"description": "Use single quotes.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"single"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Use double quotes (`\"`).",
|
||||
"description": "Use double quotes.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"double"
|
||||
@@ -1197,7 +1230,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RelatveImportsOrder": {
|
||||
"RelativeImportsOrder": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Place \"closer\" imports (fewer `.` characters, most local) before \"further\" imports (more `.` characters, least local).",
|
||||
@@ -1615,6 +1648,10 @@
|
||||
"PLE011",
|
||||
"PLE0117",
|
||||
"PLE0118",
|
||||
"PLE06",
|
||||
"PLE060",
|
||||
"PLE0604",
|
||||
"PLE0605",
|
||||
"PLE1",
|
||||
"PLE11",
|
||||
"PLE114",
|
||||
@@ -1630,6 +1667,10 @@
|
||||
"PLR04",
|
||||
"PLR040",
|
||||
"PLR0402",
|
||||
"PLR09",
|
||||
"PLR091",
|
||||
"PLR0913",
|
||||
"PLR0915",
|
||||
"PLR1",
|
||||
"PLR17",
|
||||
"PLR170",
|
||||
@@ -1726,6 +1767,10 @@
|
||||
"RET506",
|
||||
"RET507",
|
||||
"RET508",
|
||||
"RSE",
|
||||
"RSE1",
|
||||
"RSE10",
|
||||
"RSE102",
|
||||
"RUF",
|
||||
"RUF0",
|
||||
"RUF00",
|
||||
@@ -1749,6 +1794,7 @@
|
||||
"S107",
|
||||
"S108",
|
||||
"S11",
|
||||
"S110",
|
||||
"S113",
|
||||
"S3",
|
||||
"S32",
|
||||
@@ -1802,6 +1848,10 @@
|
||||
"SIM4",
|
||||
"SIM40",
|
||||
"SIM401",
|
||||
"SLF",
|
||||
"SLF0",
|
||||
"SLF00",
|
||||
"SLF001",
|
||||
"T",
|
||||
"T1",
|
||||
"T10",
|
||||
@@ -1858,7 +1908,6 @@
|
||||
"UP013",
|
||||
"UP014",
|
||||
"UP015",
|
||||
"UP016",
|
||||
"UP017",
|
||||
"UP018",
|
||||
"UP019",
|
||||
@@ -1879,6 +1928,8 @@
|
||||
"UP032",
|
||||
"UP033",
|
||||
"UP034",
|
||||
"UP035",
|
||||
"UP036",
|
||||
"W",
|
||||
"W2",
|
||||
"W29",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -53,6 +53,7 @@ similar = { version = "2.2.1" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
update-informer = { version = "0.6.0", default-features = false, features = ["pypi"], optional = true }
|
||||
walkdir = { version = "2.3.2" }
|
||||
strum = "0.24.1"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { version = "2.0.4" }
|
||||
|
||||
@@ -5,6 +5,7 @@ use regex::Regex;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::registry::Rule;
|
||||
use ruff::resolver::ConfigProcessor;
|
||||
use ruff::settings::configuration::RuleSelection;
|
||||
use ruff::settings::types::{
|
||||
FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat,
|
||||
};
|
||||
@@ -15,12 +16,50 @@ 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,
|
||||
},
|
||||
/// List all supported upstream linters
|
||||
Linter {
|
||||
/// 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"))]
|
||||
@@ -78,13 +117,13 @@ pub struct Args {
|
||||
help_heading = "Rule selection"
|
||||
)]
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
/// Like --ignore, but adds additional rule codes on top of the ignored
|
||||
/// ones.
|
||||
/// Like --ignore. (Deprecated: You can just use --ignore instead.)
|
||||
#[arg(
|
||||
long,
|
||||
value_delimiter = ',',
|
||||
value_name = "RULE_CODE",
|
||||
help_heading = "Rule selection"
|
||||
help_heading = "Rule selection",
|
||||
hide = true
|
||||
)]
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
@@ -179,81 +218,36 @@ pub struct Args {
|
||||
update_check: bool,
|
||||
#[clap(long, overrides_with("update_check"), hide = true)]
|
||||
no_update_check: bool,
|
||||
/// Show counts for every rule with at least one violation.
|
||||
#[arg(
|
||||
long,
|
||||
// Unsupported default-command arguments.
|
||||
conflicts_with = "diff",
|
||||
conflicts_with = "show_source",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub statistics: bool,
|
||||
/// Enable automatic additions of `noqa` directives to failing lines.
|
||||
#[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.
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
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.
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
@@ -263,32 +257,52 @@ 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.
|
||||
conflicts_with = "statistics",
|
||||
conflicts_with = "stdin_filename",
|
||||
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,24 +320,22 @@ 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,
|
||||
show_settings: self.show_settings,
|
||||
statistics: self.statistics,
|
||||
stdin_filename: self.stdin_filename,
|
||||
watch: self.watch,
|
||||
},
|
||||
@@ -371,17 +383,15 @@ 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,
|
||||
pub show_settings: bool,
|
||||
pub statistics: bool,
|
||||
pub stdin_filename: Option<PathBuf>,
|
||||
pub watch: bool,
|
||||
}
|
||||
@@ -433,18 +443,25 @@ impl ConfigProcessor for &Overrides {
|
||||
if let Some(fix_only) = &self.fix_only {
|
||||
config.fix_only = Some(*fix_only);
|
||||
}
|
||||
if let Some(fixable) = &self.fixable {
|
||||
config.fixable = Some(fixable.clone());
|
||||
}
|
||||
config.rule_selections.push(RuleSelection {
|
||||
select: self.select.clone(),
|
||||
ignore: self
|
||||
.ignore
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.extend_ignore.iter().cloned().into_iter())
|
||||
.flatten()
|
||||
.collect(),
|
||||
extend_select: self.extend_select.clone().unwrap_or_default(),
|
||||
fixable: self.fixable.clone(),
|
||||
unfixable: self.unfixable.clone().unwrap_or_default(),
|
||||
});
|
||||
if let Some(format) = &self.format {
|
||||
config.format = Some(*format);
|
||||
}
|
||||
if let Some(force_exclude) = &self.force_exclude {
|
||||
config.force_exclude = Some(*force_exclude);
|
||||
}
|
||||
if let Some(ignore) = &self.ignore {
|
||||
config.ignore = Some(ignore.clone());
|
||||
}
|
||||
if let Some(line_length) = &self.line_length {
|
||||
config.line_length = Some(*line_length);
|
||||
}
|
||||
@@ -454,38 +471,15 @@ impl ConfigProcessor for &Overrides {
|
||||
if let Some(respect_gitignore) = &self.respect_gitignore {
|
||||
config.respect_gitignore = Some(*respect_gitignore);
|
||||
}
|
||||
if let Some(select) = &self.select {
|
||||
config.select = Some(select.clone());
|
||||
}
|
||||
if let Some(show_source) = &self.show_source {
|
||||
config.show_source = Some(*show_source);
|
||||
}
|
||||
if let Some(target_version) = &self.target_version {
|
||||
config.target_version = Some(*target_version);
|
||||
}
|
||||
if let Some(unfixable) = &self.unfixable {
|
||||
config.unfixable = Some(unfixable.clone());
|
||||
}
|
||||
if let Some(update_check) = &self.update_check {
|
||||
config.update_check = Some(*update_check);
|
||||
}
|
||||
// Special-case: `extend_ignore` and `extend_select` are parallel arrays, so
|
||||
// push an empty array if only one of the two is provided.
|
||||
match (&self.extend_ignore, &self.extend_select) {
|
||||
(Some(extend_ignore), Some(extend_select)) => {
|
||||
config.extend_ignore.push(extend_ignore.clone());
|
||||
config.extend_select.push(extend_select.clone());
|
||||
}
|
||||
(Some(extend_ignore), None) => {
|
||||
config.extend_ignore.push(extend_ignore.clone());
|
||||
config.extend_select.push(Vec::new());
|
||||
}
|
||||
(None, Some(extend_select)) => {
|
||||
config.extend_ignore.push(Vec::new());
|
||||
config.extend_select.push(extend_select.clone());
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,16 +18,17 @@ 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;
|
||||
|
||||
pub mod linter;
|
||||
|
||||
/// Run the linter over a collection of files.
|
||||
pub fn run(
|
||||
files: &[PathBuf],
|
||||
@@ -111,7 +112,7 @@ pub fn run(
|
||||
let settings = resolver.resolve(path, pyproject_strategy);
|
||||
if settings.rules.enabled(&Rule::IOError) {
|
||||
Diagnostics::new(vec![Message {
|
||||
kind: IOError(message).into(),
|
||||
kind: IOError { message }.into(),
|
||||
location: Location::default(),
|
||||
end_location: Location::default(),
|
||||
fix: None,
|
||||
@@ -269,10 +270,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 +291,7 @@ pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
println!("* {format}");
|
||||
}
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
HelpFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
@@ -300,24 +301,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 +314,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)?;
|
||||
|
||||
59
ruff_cli/src/commands/linter.rs
Normal file
59
ruff_cli/src/commands/linter.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use itertools::Itertools;
|
||||
use serde::Serialize;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff::registry::{Linter, LinterCategory, RuleNamespace};
|
||||
|
||||
use crate::args::HelpFormat;
|
||||
|
||||
pub fn linter(format: HelpFormat) {
|
||||
match format {
|
||||
HelpFormat::Text => {
|
||||
for linter in Linter::iter() {
|
||||
let prefix = match linter.common_prefix() {
|
||||
"" => linter
|
||||
.categories()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|LinterCategory(prefix, ..)| prefix)
|
||||
.join("/"),
|
||||
prefix => prefix.to_string(),
|
||||
};
|
||||
println!("{:>4} {}", prefix, linter.name());
|
||||
}
|
||||
}
|
||||
|
||||
HelpFormat::Json => {
|
||||
let linters: Vec<_> = Linter::iter()
|
||||
.map(|linter_info| LinterInfo {
|
||||
prefix: linter_info.common_prefix(),
|
||||
name: linter_info.name(),
|
||||
categories: linter_info.categories().map(|cats| {
|
||||
cats.iter()
|
||||
.map(|LinterCategory(prefix, name, ..)| LinterCategoryInfo {
|
||||
prefix,
|
||||
name,
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
|
||||
println!("{}", serde_json::to_string_pretty(&linters).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct LinterInfo {
|
||||
prefix: &'static str,
|
||||
name: &'static str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
categories: Option<Vec<LinterCategoryInfo>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct LinterCategoryInfo {
|
||||
prefix: &'static str,
|
||||
name: &'static str,
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//! This library only exists to enable the Ruff internal tooling (`ruff_dev`)
|
||||
//! to automatically update the `ruff --help` output in the `README.md`.
|
||||
//! to automatically update the `ruff help` output in the `README.md`.
|
||||
//!
|
||||
//! For the actual Ruff library, see [`ruff`].
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -10,7 +10,21 @@ mod args;
|
||||
|
||||
use clap::CommandFactory;
|
||||
|
||||
/// Returns the output of `ruff --help`.
|
||||
pub fn help() -> String {
|
||||
/// Returns the output of `ruff help`.
|
||||
pub fn command_help() -> String {
|
||||
args::Args::command().render_help().to_string()
|
||||
}
|
||||
|
||||
/// Returns the output of `ruff help check`.
|
||||
pub fn subcommand_help() -> String {
|
||||
let mut cmd = args::Args::command();
|
||||
|
||||
// The build call is necessary for the help output to contain `Usage: ruff check` instead of `Usage: check`
|
||||
// see https://github.com/clap-rs/clap/issues/4685
|
||||
cmd.build();
|
||||
|
||||
cmd.find_subcommand_mut("check")
|
||||
.expect("`check` subcommand not found")
|
||||
.render_help()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
@@ -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,38 +35,67 @@ mod resolve;
|
||||
pub mod updates;
|
||||
|
||||
fn inner_main() -> Result<ExitCode> {
|
||||
// Extract command-line arguments.
|
||||
let args = Args::parse();
|
||||
let mut args: Vec<_> = std::env::args_os().collect();
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
eprintln!(
|
||||
r#"
|
||||
// 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 {
|
||||
command,
|
||||
log_level_args,
|
||||
} = Args::parse_from(args);
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
eprintln!(
|
||||
r#"
|
||||
{}: `ruff` crashed. This indicates a bug in `ruff`. If you could open an issue at:
|
||||
|
||||
https://github.com/charliermarsh/ruff/issues/new?title=%5BPanic%5D
|
||||
|
||||
quoting the executed command, along with the relevant file contents and `pyproject.toml` settings, we'd be very appreciative!
|
||||
"#,
|
||||
"error".red().bold(),
|
||||
);
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
"error".red().bold(),
|
||||
);
|
||||
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::Linter { format } => commands::linter::linter(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 +105,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 +126,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 +159,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.");
|
||||
}
|
||||
@@ -221,7 +248,11 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff)) {
|
||||
printer.write_once(&diagnostics)?;
|
||||
if cli.statistics {
|
||||
printer.write_statistics(&diagnostics)?;
|
||||
} else {
|
||||
printer.write_once(&diagnostics)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
@@ -244,10 +275,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() {
|
||||
|
||||
@@ -7,7 +7,7 @@ use annotate_snippets::display_list::{DisplayList, FormatOptions};
|
||||
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use itertools::iterate;
|
||||
use itertools::{iterate, Itertools};
|
||||
use ruff::fs::relativize_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
@@ -43,6 +43,13 @@ struct ExpandedMessage<'a> {
|
||||
filename: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedStatistics<'a> {
|
||||
count: usize,
|
||||
code: &'a str,
|
||||
message: String,
|
||||
}
|
||||
|
||||
struct SerializeRuleAsCode<'a>(&'a Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode<'_> {
|
||||
@@ -336,6 +343,80 @@ impl<'a> Printer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_statistics(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
let violations = diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| message.kind.rule())
|
||||
.sorted()
|
||||
.dedup()
|
||||
.collect::<Vec<_>>();
|
||||
if violations.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let statistics = violations
|
||||
.iter()
|
||||
.map(|rule| ExpandedStatistics {
|
||||
code: rule.code(),
|
||||
count: diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.filter(|message| message.kind.rule() == *rule)
|
||||
.count(),
|
||||
message: diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.find(|message| message.kind.rule() == *rule)
|
||||
.map(|message| message.kind.body())
|
||||
.unwrap(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||
match self.format {
|
||||
SerializationFormat::Text => {
|
||||
// Compute the maximum number of digits in the count and code, for all messages, to enable
|
||||
// pretty-printing.
|
||||
let count_width = num_digits(
|
||||
statistics
|
||||
.iter()
|
||||
.map(|statistic| statistic.count)
|
||||
.max()
|
||||
.unwrap(),
|
||||
);
|
||||
let code_width = statistics
|
||||
.iter()
|
||||
.map(|statistic| statistic.code.len())
|
||||
.max()
|
||||
.unwrap();
|
||||
|
||||
// By default, we mimic Flake8's `--statistics` format.
|
||||
for msg in statistics {
|
||||
writeln!(
|
||||
stdout,
|
||||
"{:>count_width$}\t{:<code_width$}\t{}",
|
||||
msg.count, msg.code, msg.message
|
||||
)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
writeln!(stdout, "{}", serde_json::to_string_pretty(&statistics)?)?;
|
||||
}
|
||||
_ => {
|
||||
anyhow::bail!(
|
||||
"Unsupported serialization format for statistics: {:?}",
|
||||
self.format
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
stdout.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_continuously(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
if matches!(self.log_level, LogLevel::Silent) {
|
||||
return Ok(());
|
||||
|
||||
@@ -14,7 +14,7 @@ const BIN_NAME: &str = "ruff";
|
||||
#[test]
|
||||
fn test_stdin_success() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--format", "text"])
|
||||
cmd.args(["-", "--format", "text", "--isolated"])
|
||||
.write_stdin("")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -25,7 +25,7 @@ fn test_stdin_success() -> Result<()> {
|
||||
fn test_stdin_error() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text"])
|
||||
.args(["-", "--format", "text", "--isolated"])
|
||||
.write_stdin("import os\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -41,7 +41,14 @@ fn test_stdin_error() -> Result<()> {
|
||||
fn test_stdin_filename() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--stdin-filename", "F401.py"])
|
||||
.args([
|
||||
"-",
|
||||
"--format",
|
||||
"text",
|
||||
"--stdin-filename",
|
||||
"F401.py",
|
||||
"--isolated",
|
||||
])
|
||||
.write_stdin("import os\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -58,7 +65,14 @@ fn test_stdin_filename() -> Result<()> {
|
||||
fn test_stdin_json() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "json", "--stdin-filename", "F401.py"])
|
||||
.args([
|
||||
"-",
|
||||
"--format",
|
||||
"json",
|
||||
"--stdin-filename",
|
||||
"F401.py",
|
||||
"--isolated",
|
||||
])
|
||||
.write_stdin("import os\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -107,7 +121,7 @@ fn test_stdin_json() -> Result<()> {
|
||||
fn test_stdin_autofix() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--fix"])
|
||||
.args(["-", "--format", "text", "--fix", "--isolated"])
|
||||
.write_stdin("import os\nimport sys\n\nprint(sys.version)\n")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -122,7 +136,7 @@ fn test_stdin_autofix() -> Result<()> {
|
||||
fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--fix"])
|
||||
.args(["-", "--format", "text", "--fix", "--isolated"])
|
||||
.write_stdin("import os\nimport sys\n\nif (1, 2):\n print(sys.version)\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -137,7 +151,7 @@ fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<(
|
||||
fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--fix"])
|
||||
.args(["-", "--format", "text", "--fix", "--isolated"])
|
||||
.write_stdin("import sys\n\nprint(sys.version)\n")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -152,7 +166,7 @@ fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()>
|
||||
fn test_show_source() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--show-source"])
|
||||
.args(["-", "--format", "text", "--show-source", "--isolated"])
|
||||
.write_stdin("l = 1")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -163,8 +177,26 @@ 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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_statistics() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
let output = cmd
|
||||
.args(["-", "--format", "text", "--select", "F401", "--statistics"])
|
||||
.write_stdin("import sys\nimport os\n\nprint(os.getuid())\n")
|
||||
.assert()
|
||||
.failure();
|
||||
assert_eq!(
|
||||
str::from_utf8(&output.get_output().stdout)?
|
||||
.lines()
|
||||
.last()
|
||||
.unwrap(),
|
||||
"1\tF401\t`sys` imported but unused"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -15,7 +15,7 @@ rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde_json = {version="1.0.91"}
|
||||
serde_json = { version = "1.0.91" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
//! Generate CLI help.
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
use anyhow::Result;
|
||||
use std::str;
|
||||
|
||||
const HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated cli help. -->";
|
||||
const HELP_END_PRAGMA: &str = "<!-- End auto-generated cli help. -->";
|
||||
const COMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated command help. -->";
|
||||
const COMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated command help. -->";
|
||||
|
||||
const SUBCOMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated subcommand help. -->";
|
||||
const SUBCOMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated subcommand help. -->";
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
@@ -19,15 +22,25 @@ fn trim_lines(s: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn main(args: &Args) -> Result<()> {
|
||||
let output = trim_lines(ruff_cli::help().trim());
|
||||
// Generate `ruff help`.
|
||||
let command_help = trim_lines(ruff_cli::command_help().trim());
|
||||
|
||||
// Generate `ruff help check`.
|
||||
let subcommand_help = trim_lines(ruff_cli::subcommand_help().trim());
|
||||
|
||||
if args.dry_run {
|
||||
print!("{output}");
|
||||
print!("{command_help}");
|
||||
print!("{subcommand_help}");
|
||||
} else {
|
||||
replace_readme_section(
|
||||
&format!("```\n{output}\n```\n"),
|
||||
HELP_BEGIN_PRAGMA,
|
||||
HELP_END_PRAGMA,
|
||||
&format!("```\n{command_help}\n```\n"),
|
||||
COMMAND_HELP_BEGIN_PRAGMA,
|
||||
COMMAND_HELP_END_PRAGMA,
|
||||
)?;
|
||||
replace_readme_section(
|
||||
&format!("```\n{subcommand_help}\n```\n"),
|
||||
SUBCOMMAND_HELP_BEGIN_PRAGMA,
|
||||
SUBCOMMAND_HELP_END_PRAGMA,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ use anyhow::Result;
|
||||
use ruff::settings::options::Options;
|
||||
use schemars::schema_for;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
|
||||
@@ -19,10 +21,7 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
if args.dry_run {
|
||||
println!("{schema_string}");
|
||||
} else {
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("ruff.schema.json");
|
||||
let file = PathBuf::from(ROOT_DIR).join("ruff.schema.json");
|
||||
fs::write(file, schema_string.as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Generate a Markdown-compatible table of supported lint rules.
|
||||
|
||||
use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
use ruff::registry::{Linter, LinterCategory, Rule, RuleNamespace};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
@@ -47,7 +48,15 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
let mut table_out = String::new();
|
||||
let mut toc_out = String::new();
|
||||
for linter in Linter::iter() {
|
||||
let codes_csv: String = linter.prefixes().join(", ");
|
||||
let codes_csv: String = match linter.common_prefix() {
|
||||
"" => linter
|
||||
.categories()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|LinterCategory(prefix, ..)| prefix)
|
||||
.join(", "),
|
||||
prefix => prefix.to_string(),
|
||||
};
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", linter.name()));
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
|
||||
@@ -30,6 +30,8 @@ mod utils;
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
const ROOT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../");
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(propagate_version = true)]
|
||||
|
||||
@@ -5,12 +5,11 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
pub fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> {
|
||||
// Read the existing file.
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("README.md");
|
||||
let file = PathBuf::from(ROOT_DIR).join("README.md");
|
||||
let existing = fs::read_to_string(&file)?;
|
||||
|
||||
// Extract the prefix.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.236"
|
||||
version = "0.0.240"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -52,6 +52,7 @@ pub fn expand<'a>(
|
||||
::strum_macros::EnumIter,
|
||||
::strum_macros::EnumString,
|
||||
::strum_macros::AsRefStr,
|
||||
::strum_macros::IntoStaticStr,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
|
||||
@@ -15,13 +15,15 @@ pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
|
||||
|
||||
let mut parsed = Vec::new();
|
||||
|
||||
let mut prefix_match_arms = quote!();
|
||||
let mut common_prefix_match_arms = quote!();
|
||||
let mut name_match_arms = quote!(Self::Ruff => "Ruff-specific rules",);
|
||||
let mut url_match_arms = quote!(Self::Ruff => None,);
|
||||
let mut into_iter_match_arms = quote!();
|
||||
|
||||
let mut all_prefixes = HashSet::new();
|
||||
|
||||
for variant in variants {
|
||||
let mut first_chars = HashSet::new();
|
||||
let prefixes: Result<Vec<_>, _> = variant
|
||||
.attrs
|
||||
.iter()
|
||||
@@ -33,7 +35,9 @@ pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
|
||||
let str = lit.value();
|
||||
match str.chars().next() {
|
||||
None => return Err(Error::new(lit.span(), "expected prefix string to be non-empty")),
|
||||
Some(_) => {},
|
||||
Some(c) => if !first_chars.insert(c) {
|
||||
return Err(Error::new(lit.span(), format!("this variant already has another prefix starting with the character '{c}'")))
|
||||
}
|
||||
}
|
||||
if !all_prefixes.insert(str.clone()) {
|
||||
return Err(Error::new(lit.span(), "prefix has already been defined before"));
|
||||
@@ -63,31 +67,43 @@ pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
|
||||
}
|
||||
|
||||
for lit in &prefixes {
|
||||
parsed.push((lit.clone(), variant_ident.clone()));
|
||||
parsed.push((
|
||||
lit.clone(),
|
||||
variant_ident.clone(),
|
||||
match prefixes.len() {
|
||||
1 => ParseStrategy::SinglePrefix,
|
||||
_ => ParseStrategy::MultiplePrefixes,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
prefix_match_arms.extend(quote! {
|
||||
Self::#variant_ident => &[#(#prefixes),*],
|
||||
});
|
||||
if let [prefix] = &prefixes[..] {
|
||||
common_prefix_match_arms.extend(quote! { Self::#variant_ident => #prefix, });
|
||||
|
||||
let prefix_ident = Ident::new(prefix, Span::call_site());
|
||||
into_iter_match_arms.extend(quote! {
|
||||
#ident::#variant_ident => RuleCodePrefix::#prefix_ident.into_iter(),
|
||||
});
|
||||
} else {
|
||||
// There is more than one prefix. We already previously asserted
|
||||
// that prefixes of the same variant don't start with the same character
|
||||
// so the common prefix for this variant is the empty string.
|
||||
common_prefix_match_arms.extend(quote! { Self::#variant_ident => "", });
|
||||
}
|
||||
}
|
||||
|
||||
parsed.sort_by_key(|(prefix, _)| Reverse(prefix.len()));
|
||||
parsed.sort_by_key(|(prefix, ..)| Reverse(prefix.len()));
|
||||
|
||||
let mut if_statements = quote!();
|
||||
let mut into_iter_match_arms = quote!();
|
||||
|
||||
for (prefix, field) in parsed {
|
||||
for (prefix, field, strategy) in parsed {
|
||||
let ret_str = match strategy {
|
||||
ParseStrategy::SinglePrefix => quote!(rest),
|
||||
ParseStrategy::MultiplePrefixes => quote!(code),
|
||||
};
|
||||
if_statements.extend(quote! {if let Some(rest) = code.strip_prefix(#prefix) {
|
||||
return Some((#ident::#field, rest));
|
||||
return Some((#ident::#field, #ret_str));
|
||||
}});
|
||||
|
||||
let prefix_ident = Ident::new(&prefix, Span::call_site());
|
||||
|
||||
if field != "Pycodestyle" {
|
||||
into_iter_match_arms.extend(quote! {
|
||||
#ident::#field => RuleCodePrefix::#prefix_ident.into_iter(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
into_iter_match_arms.extend(quote! {
|
||||
@@ -104,9 +120,8 @@ pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
fn prefixes(&self) -> &'static [&'static str] {
|
||||
match self { #prefix_match_arms }
|
||||
fn common_prefix(&self) -> &'static str {
|
||||
match self { #common_prefix_match_arms }
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
@@ -152,3 +167,8 @@ fn parse_doc_attr(doc_attr: &Attribute) -> syn::Result<(String, String)> {
|
||||
fn parse_markdown_link(link: &str) -> Option<(&str, &str)> {
|
||||
link.strip_prefix('[')?.strip_suffix(')')?.split_once("](")
|
||||
}
|
||||
|
||||
enum ParseStrategy {
|
||||
SinglePrefix,
|
||||
MultiplePrefixes,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
87
scripts/generate_mkdocs.py
Normal file
87
scripts/generate_mkdocs.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""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"),
|
||||
]
|
||||
|
||||
DOCUMENTATION_LINK: str = (
|
||||
"This README is also available as [documentation](https://beta.ruff.rs/docs/)."
|
||||
)
|
||||
|
||||
FATHOM_SCRIPT: str = (
|
||||
'<script src="https://cdn.usefathom.com/script.js" data-site="DUAEBFLB" defer>'
|
||||
"</script>"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Generate an MkDocs-compatible `docs` and `mkdocs.yml`."""
|
||||
with Path("README.md").open(encoding="utf8") as fp:
|
||||
content = fp.read()
|
||||
|
||||
# Remove the documentation link, since we're _in_ the docs.
|
||||
if DOCUMENTATION_LINK not in content:
|
||||
msg = "README.md is not in the expected format."
|
||||
raise ValueError(msg)
|
||||
content = content.replace(DOCUMENTATION_LINK, "")
|
||||
|
||||
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"},
|
||||
]
|
||||
config["extra"] = {"analytics": {"provider": "fathom"}}
|
||||
|
||||
Path(".overrides/partials/integrations/analytics").mkdir(parents=True, exist_ok=True)
|
||||
with Path(".overrides/partials/integrations/analytics/fathom.html").open("w+") as fp:
|
||||
fp.write(FATHOM_SCRIPT)
|
||||
|
||||
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]
|
||||
|
||||
71
scripts/transform_readme.py
Normal file
71
scripts/transform_readme.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""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">
|
||||
<img alt="Shows a bar chart with benchmark results." src="https://user-images.githubusercontent.com/1309177/212613257-5f4bca12-6d6b-4c79-9bac-51a4c6d08928.svg">
|
||||
</p>
|
||||
"""
|
||||
|
||||
# https://squidfunk.github.io/mkdocs-material/reference/images/#light-and-dark-mode
|
||||
MK_DOCS = """
|
||||
<p 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">
|
||||
</p>
|
||||
|
||||
<p 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">
|
||||
</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)
|
||||
2
setup.py
2
setup.py
@@ -22,3 +22,5 @@ sys.exit(1)
|
||||
# To be removed once GitHub catches up.
|
||||
|
||||
setup(name="ruff", install_requires=[])
|
||||
if True: a = 1; \
|
||||
b = 2
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user