Compare commits
3 Commits
v0.0.263
...
github-292
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e97c9438a | ||
|
|
ea86edf12e | ||
|
|
2558384817 |
@@ -1,6 +1,5 @@
|
||||
[alias]
|
||||
dev = "run --package ruff_dev --bin ruff_dev"
|
||||
benchmark = "bench -p ruff_benchmark --"
|
||||
|
||||
[target.'cfg(all())']
|
||||
rustflags = [
|
||||
@@ -26,11 +25,4 @@ rustflags = [
|
||||
"-Wclippy::print_stdout",
|
||||
"-Wclippy::print_stderr",
|
||||
"-Wclippy::dbg_macro",
|
||||
"-Wclippy::empty_drop",
|
||||
"-Wclippy::empty_structs_with_brackets",
|
||||
"-Wclippy::exit",
|
||||
"-Wclippy::get_unwrap",
|
||||
"-Wclippy::rc_buffer",
|
||||
"-Wclippy::rc_mutex",
|
||||
"-Wclippy::rest_pat_in_fully_bound_structs",
|
||||
]
|
||||
|
||||
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@@ -1,11 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "12:00"
|
||||
timezone: "America/New_York"
|
||||
commit-message:
|
||||
prefix: "ci(deps)"
|
||||
135
.github/workflows/benchmark.yaml
vendored
135
.github/workflows/benchmark.yaml
vendored
@@ -1,135 +0,0 @@
|
||||
name: Benchmark
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
run-benchmark:
|
||||
if: github.event_name == 'pull_request'
|
||||
name: "Run | ${{ matrix.os }}"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: "PR - Checkout Branch"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: "PR - Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: "PR - Build benchmarks"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: -p ruff_benchmark --no-run
|
||||
|
||||
- name: "PR - Run benchmarks"
|
||||
run: cargo benchmark --save-baseline=pr
|
||||
|
||||
- name: "Main - Checkout Branch"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
clean: false
|
||||
ref: main
|
||||
|
||||
- name: "Main - Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- name: "Main - Build benchmarks"
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: bench
|
||||
args: -p ruff_benchmark --no-run
|
||||
|
||||
- name: "Main - Run benchmarks"
|
||||
run: cargo benchmark --save-baseline=main
|
||||
|
||||
- name: "Upload benchmark results"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-${{ matrix.os }}
|
||||
path: ./target/criterion
|
||||
|
||||
# Cleanup
|
||||
- name: Remove Criterion Artifact
|
||||
uses: JesseTG/rm@v1.0.3
|
||||
with:
|
||||
path: ./target/criterion
|
||||
|
||||
benchmark-compare:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
name: Compare
|
||||
needs:
|
||||
- run-benchmark
|
||||
|
||||
steps:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- name: "Install cargo-binstall"
|
||||
uses: taiki-e/install-action@cargo-binstall
|
||||
|
||||
- name: "Install critcmp"
|
||||
run: cargo binstall critcmp -y
|
||||
|
||||
- name: "Linux | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-ubuntu-latest
|
||||
path: ./target/criterion
|
||||
|
||||
- name: "Linux | Compare benchmark results"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "### Benchmark" >> summary.md
|
||||
echo "#### Linux" >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
critcmp main pr >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
echo "" >> summary.md
|
||||
|
||||
- name: "Linux | Cleanup benchmark results"
|
||||
run: rm -rf ./target/criterion
|
||||
|
||||
- name: "Windows | Download PR benchmark results"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: benchmark-results-windows-latest
|
||||
path: ./target/criterion
|
||||
|
||||
- name: "Windows | Compare benchmark results"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "#### Windows" >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
critcmp main pr >> summary.md
|
||||
echo "\`\`\`" >> summary.md
|
||||
echo "" >> summary.md
|
||||
|
||||
echo ${{ github.event.pull_request.number }} > pr-number
|
||||
|
||||
cat summary.md > $GITHUB_STEP_SUMMARY
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload PR Number
|
||||
with:
|
||||
name: pr-number
|
||||
path: pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload Summary
|
||||
with:
|
||||
name: summary
|
||||
path: summary.md
|
||||
174
.github/workflows/ci.yaml
vendored
174
.github/workflows/ci.yaml
vendored
@@ -17,6 +17,20 @@ env:
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
cargo-build:
|
||||
name: "cargo build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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 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 docs
|
||||
|
||||
cargo-fmt:
|
||||
name: "cargo fmt"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -34,7 +48,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: |
|
||||
rustup component add clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
|
||||
cargo-clippy-wasm:
|
||||
@@ -46,8 +60,8 @@ jobs:
|
||||
run: |
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
|
||||
cargo-test:
|
||||
strategy:
|
||||
@@ -59,9 +73,9 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo install cargo-insta
|
||||
- run: pip install black[d]==23.1.0
|
||||
- run: pip install black[d]==22.12.0
|
||||
- name: "Run tests (Ubuntu)"
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
@@ -79,30 +93,6 @@ jobs:
|
||||
env:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
cargo-test-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
name: "cargo test (wasm)"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "npm"
|
||||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run wasm-pack"
|
||||
run: |
|
||||
cd crates/ruff_wasm
|
||||
wasm-pack test --node
|
||||
|
||||
scripts:
|
||||
name: "test scripts"
|
||||
@@ -110,16 +100,29 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup component add rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --prefix PL --code C0999 --linter pylint
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
- run: |
|
||||
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
|
||||
./scripts/add_rule.py --name FirstRule --prefix TST --code 001 --linter test
|
||||
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
|
||||
maturin-build:
|
||||
name: "maturin build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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"
|
||||
@@ -129,104 +132,3 @@ jobs:
|
||||
- uses: crate-ci/typos@master
|
||||
with:
|
||||
files: .
|
||||
|
||||
ecosystem:
|
||||
name: "ecosystem"
|
||||
runs-on: ubuntu-latest
|
||||
needs: cargo-test
|
||||
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download Ruff binary
|
||||
id: ruff-target
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
name: Download base results
|
||||
with:
|
||||
name: ruff
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
check_artifacts: true
|
||||
|
||||
- name: Run ecosystem check
|
||||
run: |
|
||||
# Make executable, since artifact download doesn't preserve this
|
||||
chmod +x ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
|
||||
|
||||
scripts/check_ecosystem.py ruff ${{ steps.ruff-target.outputs.download-path }}/ruff | tee ecosystem-result
|
||||
cat ecosystem-result > $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo ${{ github.event.number }} > pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload PR Number
|
||||
with:
|
||||
name: pr-number
|
||||
path: pr-number
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: Upload Results
|
||||
with:
|
||||
name: ecosystem-result
|
||||
path: ecosystem-result
|
||||
|
||||
cargo-udeps:
|
||||
name: "cargo udeps"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install nightly Rust toolchain"
|
||||
# Only pinned to make caching work, update freely
|
||||
run: rustup toolchain install nightly-2023-03-30
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
run: |
|
||||
unused_dependencies=$(cargo +nightly-2023-03-30 udeps > unused.txt && cat unused.txt | cut -d $'\n' -f 2-)
|
||||
if [ -z "$unused_dependencies" ]; then
|
||||
echo "No unused dependencies found" > $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
else
|
||||
echo "Found unused dependencies" > $GITHUB_STEP_SUMMARY
|
||||
echo '```console' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$unused_dependencies" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pre-commit:
|
||||
name: "pre-commit"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install pre-commit"
|
||||
run: pip install pre-commit
|
||||
- name: "Cache pre-commit"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
- name: "Run pre-commit"
|
||||
run: |
|
||||
echo '```console' > $GITHUB_STEP_SUMMARY
|
||||
# Enable color output for pre-commit and remove it for the summary
|
||||
SKIP=cargo-fmt,clippy,dev-generate-all pre-commit run --all-files --show-diff-on-failure --color=always | \
|
||||
tee >(sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGK]//g' >> $GITHUB_STEP_SUMMARY) >&1
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
exit $exit_code
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
4
.github/workflows/flake8-to-ruff.yaml
vendored
4
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -133,7 +133,7 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --no-default-features --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
- uses: uraimo/run-on-arch-action@v2.5.0
|
||||
if: matrix.target != 'ppc64'
|
||||
name: Install built wheel
|
||||
with:
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
- uses: uraimo/run-on-arch-action@master
|
||||
name: Install built wheel
|
||||
with:
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
|
||||
2
.github/workflows/playground.yaml
vendored
2
.github/workflows/playground.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- uses: jetli/wasm-bindgen-action@v0.2.0
|
||||
- name: "Run wasm-pack"
|
||||
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff_wasm
|
||||
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff
|
||||
- name: "Install Node dependencies"
|
||||
run: npm ci
|
||||
working-directory: playground
|
||||
|
||||
84
.github/workflows/pr-comment.yaml
vendored
84
.github/workflows/pr-comment.yaml
vendored
@@ -1,84 +0,0 @@
|
||||
name: PR Check Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI, Benchmark]
|
||||
types: [completed]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
workflow_run_id:
|
||||
description: The ecosystem workflow that triggers the workflow run
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
name: Download PR Number
|
||||
with:
|
||||
name: pr-number
|
||||
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
|
||||
if_no_artifact_found: ignore
|
||||
|
||||
- name: Extract PR Number
|
||||
id: pr-number
|
||||
run: |
|
||||
if [[ -f pr-number ]]
|
||||
then
|
||||
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
name: "Download Ecosystem Result"
|
||||
id: download-ecosystem-result
|
||||
if: steps.pr-number.outputs.pr-number
|
||||
with:
|
||||
name: ecosystem-result
|
||||
workflow: ci.yaml
|
||||
pr: ${{ steps.pr-number.outputs.pr-number }}
|
||||
path: pr/ecosystem
|
||||
if_no_artifact_found: ignore
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
name: "Download Benchmark Result"
|
||||
id: download-benchmark-result
|
||||
if: steps.pr-number.outputs.pr-number
|
||||
with:
|
||||
name: summary
|
||||
workflow: benchmark.yaml
|
||||
pr: ${{ steps.pr-number.outputs.pr-number }}
|
||||
path: pr/benchmark
|
||||
if_no_artifact_found: ignore
|
||||
|
||||
- name: Generate Comment
|
||||
id: generate-comment
|
||||
if: steps.download-ecosystem-result.outputs.found_artifact == 'true' || steps.download-benchmark-result.outputs.found_artifact == 'true'
|
||||
run: |
|
||||
echo 'comment<<EOF' >> $GITHUB_OUTPUT
|
||||
echo '## PR Check Results' >> $GITHUB_OUTPUT
|
||||
|
||||
if [[ -f pr/ecosystem/ecosystem-result ]]
|
||||
then
|
||||
echo "### Ecosystem" >> $GITHUB_OUTPUT
|
||||
cat pr/ecosystem/ecosystem-result >> $GITHUB_OUTPUT
|
||||
echo "" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ -f pr/benchmark/summary.md ]]
|
||||
then
|
||||
cat pr/benchmark/summary.md >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
echo 'EOF' >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create or update comment
|
||||
if: steps.generate-comment.outputs.comment
|
||||
uses: thollander/actions-comment-pull-request@v2
|
||||
with:
|
||||
pr_number: ${{ steps.pr-number.outputs.pr-number }}
|
||||
message: ${{ steps.generate-comment.outputs.comment }}
|
||||
comment_tag: PR Check Results
|
||||
4
.github/workflows/ruff.yaml
vendored
4
.github/workflows/ruff.yaml
vendored
@@ -208,7 +208,7 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
- uses: uraimo/run-on-arch-action@v2.5.0
|
||||
if: matrix.platform.arch != 'ppc64'
|
||||
name: Install built wheel
|
||||
with:
|
||||
@@ -309,7 +309,7 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
- uses: uraimo/run-on-arch-action@master
|
||||
name: Install built wheel
|
||||
with:
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,7 +3,6 @@
|
||||
crates/ruff/resources/test/cpython
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
github_search.jsonl
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
fail_fast: true
|
||||
repos:
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.12.1
|
||||
rev: v0.10.1
|
||||
hooks:
|
||||
- id: validate-pyproject
|
||||
|
||||
@@ -28,17 +28,17 @@ repos:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --
|
||||
language: system
|
||||
language: rust
|
||||
types: [rust]
|
||||
- id: clippy
|
||||
name: clippy
|
||||
entry: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
language: system
|
||||
language: rust
|
||||
pass_filenames: false
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: cargo run -p ruff_cli -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
|
||||
language: system
|
||||
language: rust
|
||||
types_or: [python, pyi]
|
||||
require_serial: true
|
||||
exclude: |
|
||||
@@ -49,7 +49,7 @@ repos:
|
||||
- id: dev-generate-all
|
||||
name: dev-generate-all
|
||||
entry: cargo dev generate-all
|
||||
language: system
|
||||
language: rust
|
||||
pass_filenames: false
|
||||
exclude: target
|
||||
|
||||
|
||||
@@ -1,40 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.260
|
||||
|
||||
### Fixes are now represented as a list of edits ([#3709](https://github.com/charliermarsh/ruff/pull/3709))
|
||||
|
||||
Previously, Ruff represented each fix as a single edit, which prohibited Ruff from automatically
|
||||
fixing violations that required multiple edits across a file. As such, Ruff now represents each
|
||||
fix as a list of edits.
|
||||
|
||||
This primarily affects the JSON API. Ruff's JSON representation used to represent the `fix` field as
|
||||
a single edit, like so:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Remove unused import: `sys`",
|
||||
"content": "",
|
||||
"location": {"row": 1, "column": 0},
|
||||
"end_location": {"row": 2, "column": 0}
|
||||
}
|
||||
```
|
||||
|
||||
The updated representation instead includes a list of edits:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Remove unused import: `sys`",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
"location": {"row": 1, "column": 0},
|
||||
"end_location": {"row": 2, "column": 0},
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 0.0.246
|
||||
|
||||
### `multiple-statements-on-one-line-def` (`E704`) was removed ([#2773](https://github.com/charliermarsh/ruff/pull/2773))
|
||||
@@ -180,4 +145,4 @@ default.
|
||||
`pyproject.toml` files are now resolved hierarchically, such that for each Python file, we find
|
||||
the first `pyproject.toml` file in its path, and use that to determine its lint settings.
|
||||
|
||||
See the [documentation](https://beta.ruff.rs/docs/configuration/#python-file-discovery) for more.
|
||||
See the [README](https://github.com/charliermarsh/ruff#pyprojecttoml-discovery) for more.
|
||||
|
||||
@@ -104,7 +104,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
|
||||
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
|
||||
1. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
|
||||
1. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
|
||||
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
|
||||
checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks), `crates/ruff/src/checkers/lines.rs`
|
||||
@@ -115,8 +115,9 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
contain a struct defined via `#[violation]`, along with a function that creates the violation
|
||||
based on any required inputs.
|
||||
contain a struct defined via `define_violation!`, along with a function that creates the violation
|
||||
based on any required inputs. (Many of the existing examples live in `crates/ruff/src/violations.rs`,
|
||||
but we're looking to place new rules in their own files.)
|
||||
|
||||
To trigger the violation, you'll likely want to augment the logic in `crates/ruff/src/checkers/ast.rs`,
|
||||
which defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
|
||||
@@ -214,20 +215,6 @@ 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).
|
||||
|
||||
## Ecosystem CI
|
||||
|
||||
GitHub Actions will run your changes against a number of real-world projects from GitHub and
|
||||
report on any diagnostic differences. You can also run those checks locally via:
|
||||
|
||||
```shell
|
||||
python scripts/check_ecosystem.py path/to/your/ruff path/to/older/ruff
|
||||
```
|
||||
|
||||
You can also run the Ecosystem CI check in a Docker container across a larger set of projects by
|
||||
downloading the [`known-github-tomls.json`](https://github.com/akx/ruff-usage-aggregate/blob/master/data/known-github-tomls.jsonl)
|
||||
as `github_search.jsonl` and following the instructions in [scripts/Dockerfile.ecosystem](scripts/Dockerfile.ecosystem).
|
||||
Note that this check will take a while to run.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
First, clone [CPython](https://github.com/python/cpython). It's a large and diverse Python codebase,
|
||||
|
||||
1189
Cargo.lock
generated
1189
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
61
Cargo.toml
61
Cargo.toml
@@ -3,54 +3,31 @@ members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.69"
|
||||
homepage = "https://beta.ruff.rs/docs/"
|
||||
documentation = "https://beta.ruff.rs/docs/"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
rust-version = "1.67.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
bitflags = { version = "2.1.0" }
|
||||
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
filetime = { version = "0.2.20" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.10" }
|
||||
ignore = { version = "0.4.20" }
|
||||
insta = { version = "1.28.0" }
|
||||
is-macro = { version = "0.2.2" }
|
||||
anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
itertools = { version = "0.10.5" }
|
||||
is-macro = { version = "0.2.2" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
|
||||
log = { version = "0.4.17" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
once_cell = { version = "1.17.1" }
|
||||
path-absolutize = { version = "3.0.14" }
|
||||
proc-macro2 = { version = "1.0.51" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.7.1" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
regex = { version = "1.6.0" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
|
||||
rustpython-parser = { features = [
|
||||
"lalrpop",
|
||||
"serde",
|
||||
], git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93", features = ["preserve_order"] }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.2.1" }
|
||||
smallvec = { version = "1.10.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "aa8336ee94492b52458ed8e1517238e5c6c2914c" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "aa8336ee94492b52458ed8e1517238e5c6c2914c" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
syn = { version = "2.0.15" }
|
||||
test-case = { version = "3.0.0" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
toml = { version = "0.7.2" }
|
||||
toml = { version = "0.6.0" }
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
panic = "abort"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
@@ -62,9 +39,3 @@ opt-level = 3
|
||||
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
|
||||
[profile.dev.package.rustpython-parser]
|
||||
opt-level = 1
|
||||
|
||||
# Use the `--profile release-debug` flag to show symbols in release mode.
|
||||
# e.g. `cargo build --profile release-debug`
|
||||
[profile.release-debug]
|
||||
inherits = "release"
|
||||
debug = 1
|
||||
|
||||
35
LICENSE
35
LICENSE
@@ -195,15 +195,6 @@ are:
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-gettext, licensed as follows:
|
||||
"""
|
||||
BSD Zero Clause License
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-implicit-str-concat, licensed as follows:
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
@@ -402,6 +393,7 @@ are:
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
|
||||
|
||||
- autoflake, licensed as follows:
|
||||
"""
|
||||
Copyright (C) 2012-2018 Steven Myint
|
||||
@@ -425,31 +417,6 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- autotyping, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Jelle Zijlstra
|
||||
|
||||
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, licensed as follows:
|
||||
"""
|
||||
== Flake8 License (MIT) ==
|
||||
|
||||
162
README.md
162
README.md
@@ -47,16 +47,16 @@ all while executing tens or hundreds of times faster than any individual tool.
|
||||
|
||||
Ruff is extremely actively developed and used in major open-source projects like:
|
||||
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [pandas](https://github.com/pandas-dev/pandas)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Hugging Face](https://github.com/huggingface/transformers)
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
- [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
|
||||
...and many more.
|
||||
|
||||
Ruff is backed by [Astral](https://astral.sh). Read the [launch post](https://astral.sh/blog/announcing-astral-the-company-behind-ruff),
|
||||
or the original [project announcement](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster) or
|
||||
the most recent [project update](https://notes.crmarsh.com/ruff-the-first-200-releases).
|
||||
|
||||
## Testimonials
|
||||
|
||||
@@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.263'
|
||||
rev: 'v0.0.253'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -145,20 +145,6 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
Ruff can also be used as a [VS Code extension](https://github.com/charliermarsh/ruff-vscode) or
|
||||
alongside any other editor through the [Ruff LSP](https://github.com/charliermarsh/ruff-lsp).
|
||||
|
||||
Ruff can also be used as a [GitHub Action](https://github.com/features/actions) via
|
||||
[`ruff-action`](https://github.com/chartboost/ruff-action):
|
||||
|
||||
```yaml
|
||||
name: Ruff
|
||||
on: [ push, pull_request ]
|
||||
jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: chartboost/ruff-action@v1
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml` file (see:
|
||||
@@ -174,7 +160,7 @@ select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "..."]
|
||||
unfixable = []
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
@@ -241,55 +227,6 @@ stylistic rules made obsolete by the use of an autoformatter, like
|
||||
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
|
||||
catches a wide variety of common errors (like unused imports) with zero configuration.
|
||||
|
||||
Beyond the defaults, Ruff re-implements some of the most popular Flake8 plugins and related code
|
||||
quality tools, including:
|
||||
|
||||
- [autoflake](https://pypi.org/project/autoflake/)
|
||||
- [eradicate](https://pypi.org/project/eradicate/)
|
||||
- [flake8-2020](https://pypi.org/project/flake8-2020/)
|
||||
- [flake8-annotations](https://pypi.org/project/flake8-annotations/)
|
||||
- [flake8-bandit](https://pypi.org/project/flake8-bandit/) ([#1646](https://github.com/charliermarsh/ruff/issues/1646))
|
||||
- [flake8-blind-except](https://pypi.org/project/flake8-blind-except/)
|
||||
- [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [flake8-bugbear](https://pypi.org/project/flake8-bugbear/)
|
||||
- [flake8-builtins](https://pypi.org/project/flake8-builtins/)
|
||||
- [flake8-commas](https://pypi.org/project/flake8-commas/)
|
||||
- [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [flake8-datetimez](https://pypi.org/project/flake8-datetimez/)
|
||||
- [flake8-debugger](https://pypi.org/project/flake8-debugger/)
|
||||
- [flake8-django](https://pypi.org/project/flake8-django/)
|
||||
- [flake8-docstrings](https://pypi.org/project/flake8-docstrings/)
|
||||
- [flake8-eradicate](https://pypi.org/project/flake8-eradicate/)
|
||||
- [flake8-errmsg](https://pypi.org/project/flake8-errmsg/)
|
||||
- [flake8-executable](https://pypi.org/project/flake8-executable/)
|
||||
- [flake8-gettext](https://pypi.org/project/flake8-gettext/)
|
||||
- [flake8-implicit-str-concat](https://pypi.org/project/flake8-implicit-str-concat/)
|
||||
- [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
|
||||
- [flake8-no-pep420](https://pypi.org/project/flake8-no-pep420)
|
||||
- [flake8-pie](https://pypi.org/project/flake8-pie/)
|
||||
- [flake8-print](https://pypi.org/project/flake8-print/)
|
||||
- [flake8-pyi](https://pypi.org/project/flake8-pyi/)
|
||||
- [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style/)
|
||||
- [flake8-quotes](https://pypi.org/project/flake8-quotes/)
|
||||
- [flake8-raise](https://pypi.org/project/flake8-raise/)
|
||||
- [flake8-return](https://pypi.org/project/flake8-return/)
|
||||
- [flake8-self](https://pypi.org/project/flake8-self/)
|
||||
- [flake8-simplify](https://pypi.org/project/flake8-simplify/)
|
||||
- [flake8-super](https://pypi.org/project/flake8-super/)
|
||||
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
|
||||
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
|
||||
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
|
||||
- [isort](https://pypi.org/project/isort/)
|
||||
- [mccabe](https://pypi.org/project/mccabe/)
|
||||
- [pandas-vet](https://pypi.org/project/pandas-vet/)
|
||||
- [pep8-naming](https://pypi.org/project/pep8-naming/)
|
||||
- [pydocstyle](https://pypi.org/project/pydocstyle/)
|
||||
- [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) ([#980](https://github.com/charliermarsh/ruff/issues/980))
|
||||
- [pyupgrade](https://pypi.org/project/pyupgrade/)
|
||||
- [tryceratops](https://pypi.org/project/tryceratops/)
|
||||
- [yesqa](https://pypi.org/project/yesqa/)
|
||||
|
||||
<!-- End section: Rules -->
|
||||
|
||||
For a complete enumeration of the supported rules, see [_Rules_](https://beta.ruff.rs/docs/rules/).
|
||||
@@ -332,68 +269,43 @@ Ruff is released under the MIT license.
|
||||
|
||||
## Who's Using Ruff?
|
||||
|
||||
Ruff is used by a number of major open-source projects and companies, including:
|
||||
Ruff is used in a number of major open-source projects, including:
|
||||
|
||||
- Amazon ([AWS SAM](https://github.com/aws/serverless-application-model))
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- AstraZeneca ([Magnus](https://github.com/AstraZeneca/magnus-core))
|
||||
- Benchling ([Refac](https://github.com/benchling/refac))
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
- [pandas](https://github.com/pandas-dev/pandas)
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers), [Datasets](https://github.com/huggingface/datasets), [Diffusers](https://github.com/huggingface/diffusers))
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [LangChain](https://github.com/hwchase17/langchain)
|
||||
- [LlamaIndex](https://github.com/jerryjliu/llama_index)
|
||||
- Matrix ([Synapse](https://github.com/matrix-org/synapse))
|
||||
- Meltano ([Meltano CLI](https://github.com/meltano/meltano), [Singer SDK](https://github.com/meltano/sdk))
|
||||
- Modern Treasury ([Python SDK](https://github.com/Modern-Treasury/modern-treasury-python-sdk))
|
||||
- Mozilla ([Firefox](https://github.com/mozilla/gecko-dev))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
- Microsoft ([Semantic Kernel](https://github.com/microsoft/semantic-kernel), [ONNX Runtime](https://github.com/microsoft/onnxruntime))
|
||||
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
|
||||
- [Neon](https://github.com/neondatabase/neon)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [PDM](https://github.com/pdm-project/pdm)
|
||||
- [PaddlePaddle](https://github.com/PaddlePaddle/Paddle)
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
- [Poetry](https://github.com/python-poetry/poetry)
|
||||
- [Polars](https://github.com/pola-rs/polars)
|
||||
- [PostHog](https://github.com/PostHog/posthog)
|
||||
- Prefect ([Python SDK](https://github.com/PrefectHQ/prefect), [Marvin](https://github.com/PrefectHQ/marvin))
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [PyInstaller](https://github.com/pyinstaller/pyinstaller)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Pynecone](https://github.com/pynecone-io/pynecone)
|
||||
- [Robyn](https://github.com/sansyrox/robyn)
|
||||
- Scale AI ([Launch SDK](https://github.com/scaleapi/launch-python-client))
|
||||
- Snowflake ([SnowCLI](https://github.com/Snowflake-Labs/snowcli))
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [Transformers (Hugging Face)](https://github.com/huggingface/transformers)
|
||||
- [Diffusers (Hugging Face)](https://github.com/huggingface/diffusers)
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
- [Starlite](https://github.com/starlite-api/starlite)
|
||||
- [The Algorithms](https://github.com/TheAlgorithms/Python)
|
||||
- [Vega-Altair](https://github.com/altair-viz/altair)
|
||||
- WordPress ([Openverse](https://github.com/WordPress/openverse))
|
||||
- [ZenML](https://github.com/zenml-io/zenml)
|
||||
- [Zulip](https://github.com/zulip/zulip)
|
||||
- [build (PyPA)](https://github.com/pypa/build)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Dagster](https://github.com/dagster-io/dagster)
|
||||
- [Dagger](https://github.com/dagger/dagger)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [PDM](https://github.com/pdm-project/pdm)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [ONNX](https://github.com/onnx/onnx)
|
||||
- [Polars](https://github.com/pola-rs/polars)
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Synapse (Matrix)](https://github.com/matrix-org/synapse)
|
||||
- [SnowCLI (Snowflake)](https://github.com/Snowflake-Labs/snowcli)
|
||||
- [Dispatch (Netflix)](https://github.com/Netflix/dispatch)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [Pynecone](https://github.com/pynecone-io/pynecone)
|
||||
- [OpenBB](https://github.com/OpenBB-finance/OpenBBTerminal)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [cibuildwheel (PyPA)](https://github.com/pypa/cibuildwheel)
|
||||
- [delta-rs](https://github.com/delta-io/delta-rs)
|
||||
- [build (PyPA)](https://github.com/pypa/build)
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- [featuretools](https://github.com/alteryx/featuretools)
|
||||
- [meson-python](https://github.com/mesonbuild/meson-python)
|
||||
- [nox](https://github.com/wntrblm/nox)
|
||||
- [ZenML](https://github.com/zenml-io/zenml)
|
||||
- [delta-rs](https://github.com/delta-io/delta-rs)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -5,4 +5,3 @@ extend-exclude = ["snapshots", "black"]
|
||||
trivias = "trivias"
|
||||
hel = "hel"
|
||||
whos = "whos"
|
||||
spawnve = "spawnve"
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.263"
|
||||
version = "0.0.253"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
ruff = { path = "../ruff", default-features = false }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
colored = { version = "2.0.0" }
|
||||
configparser = { version = "3.0.2" }
|
||||
once_cell = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
ruff = { path = "../ruff", default-features = false }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -85,9 +85,8 @@ flake8-to-ruff path/to/.flake8 --plugin flake8-builtins --plugin flake8-quotes
|
||||
ignore unsupported options in the `.flake8` file (or equivalent). (Similarly, Ruff has a few
|
||||
configuration options that don't exist in Flake8.)
|
||||
1. Ruff will omit any rule codes that are unimplemented or unsupported by Ruff, including rule
|
||||
codes from unsupported plugins. (See the
|
||||
[documentation](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) for the complete
|
||||
list of supported plugins.)
|
||||
codes from unsupported plugins. (See the [Ruff README](https://github.com/charliermarsh/ruff#user-content-how-does-ruff-compare-to-flake8)
|
||||
for the complete list of supported plugins.)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -46,15 +46,8 @@ fn main() -> Result<()> {
|
||||
.map(|tool| ExternalConfig {
|
||||
black: tool.black.as_ref(),
|
||||
isort: tool.isort.as_ref(),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let external_config = ExternalConfig {
|
||||
project: pyproject
|
||||
.as_ref()
|
||||
.and_then(|pyproject| pyproject.project.as_ref()),
|
||||
..external_config
|
||||
};
|
||||
|
||||
// Create Ruff's pyproject.toml section.
|
||||
let pyproject = flake8_to_ruff::convert(&config, &external_config, args.plugin)?;
|
||||
|
||||
@@ -1,84 +1,83 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.263"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
documentation.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
version = "0.0.253"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
documentation = "https://github.com/charliermarsh/ruff"
|
||||
homepage = "https://github.com/charliermarsh/ruff"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast" }
|
||||
ruff_python_semantic = { path = "../ruff_python_semantic" }
|
||||
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
|
||||
ruff_python = { path = "../ruff_python" }
|
||||
ruff_rustpython = { path = "../ruff_rustpython" }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
||||
colored = { workspace = true }
|
||||
dirs = { version = "5.0.0" }
|
||||
bisection = { version = "0.1.0" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
cfg-if = { version = "1.0.0" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { workspace = true, features = ["derive", "env", "string"] }
|
||||
colored = { version = "2.0.0" }
|
||||
derivative = { version = "2.2.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
glob = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
ignore = { workspace = true }
|
||||
imperative = { version = "1.0.4" }
|
||||
glob = { version = "0.3.0" }
|
||||
globset = { version = "0.4.9" }
|
||||
ignore = { version = "0.4.18" }
|
||||
imperative = { version = "1.0.3" }
|
||||
is-macro = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
log = { workspace = true }
|
||||
log = { version = "0.4.17" }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { workspace = true }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = { version = "0.2.15" }
|
||||
num-traits = "0.2.15"
|
||||
once_cell = { workspace = true }
|
||||
path-absolutize = { workspace = true, features = [
|
||||
"once_cell_cache",
|
||||
"use_unix_paths_on_wasm",
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.3.1", features = ["serde"] }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
result-like = "0.4.6"
|
||||
rustc-hash = { workspace = true }
|
||||
rustpython-common = { workspace = true }
|
||||
rustpython-parser = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
similar = { workspace = true, features = ["inline"] }
|
||||
shellexpand = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
smallvec = { version = "1.10.0" }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
thiserror = { version = "1.0.38" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
thiserror = { version = "1.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { version = "0.1.10" }
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
console_error_panic_hook = { version = "0.1.7" }
|
||||
console_log = { version = "0.2.0" }
|
||||
serde-wasm-bindgen = { version = "0.4" }
|
||||
js-sys = { version = "0.3.60" }
|
||||
wasm-bindgen = { version = "0.2.83" }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { workspace = true }
|
||||
pretty_assertions = "1.3.0"
|
||||
test-case = { workspace = true }
|
||||
# Disable colored output in tests
|
||||
colored = { workspace = true, features = ["no-color"] }
|
||||
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
|
||||
test-case = { version = "2.2.2" }
|
||||
wasm-bindgen-test = { version = "0.3.33" }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
|
||||
criterion = { version = "0.4.0" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
logical_lines = []
|
||||
jupyter_notebook = []
|
||||
|
||||
@@ -2,7 +2,3 @@ avoid-*
|
||||
do-not-*
|
||||
uses-*
|
||||
*-used
|
||||
rewrite-*
|
||||
prefer-*
|
||||
consider-*
|
||||
use-*
|
||||
|
||||
@@ -9,7 +9,6 @@ def foo(x, y, z):
|
||||
print(x, y, z)
|
||||
|
||||
# This is a real comment.
|
||||
# # This is a (nested) comment.
|
||||
#return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
class Foo:
|
||||
def __str__(self):
|
||||
...
|
||||
|
||||
def __repr__(self):
|
||||
...
|
||||
|
||||
def __len__(self):
|
||||
...
|
||||
|
||||
def __length_hint__(self):
|
||||
...
|
||||
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
def __del__(self):
|
||||
...
|
||||
|
||||
def __bool__(self):
|
||||
...
|
||||
|
||||
def __bytes__(self):
|
||||
...
|
||||
|
||||
def __format__(self, format_spec):
|
||||
...
|
||||
|
||||
def __contains__(self, item):
|
||||
...
|
||||
|
||||
def __complex__(self):
|
||||
...
|
||||
|
||||
def __int__(self):
|
||||
...
|
||||
|
||||
def __float__(self):
|
||||
...
|
||||
|
||||
def __index__(self):
|
||||
...
|
||||
@@ -1,13 +1,11 @@
|
||||
assert True # S101
|
||||
|
||||
# Error
|
||||
assert True
|
||||
|
||||
def fn():
|
||||
x = 1
|
||||
assert x == 1 # S101
|
||||
assert x == 2 # S101
|
||||
|
||||
# Error
|
||||
assert x == 1
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert True # OK
|
||||
# Error
|
||||
assert x == 2
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import pickle
|
||||
|
||||
pickle.loads()
|
||||
@@ -1,3 +0,0 @@
|
||||
from telnetlib import Telnet
|
||||
|
||||
Telnet("localhost", 23)
|
||||
@@ -1,20 +0,0 @@
|
||||
from subprocess import Popen, call, check_call, check_output, run
|
||||
|
||||
# Check different Popen wrappers are checked.
|
||||
Popen("true", shell=True)
|
||||
call("true", shell=True)
|
||||
check_call("true", shell=True)
|
||||
check_output("true", shell=True)
|
||||
run("true", shell=True)
|
||||
|
||||
# Check values that truthy values are treated as true.
|
||||
Popen("true", shell=1)
|
||||
Popen("true", shell=[1])
|
||||
Popen("true", shell={1: 1})
|
||||
Popen("true", shell=(1,))
|
||||
|
||||
# Check command argument looks unsafe.
|
||||
var_string = "true"
|
||||
Popen(var_string, shell=True)
|
||||
Popen([var_string], shell=True)
|
||||
Popen([var_string, ""], shell=True)
|
||||
@@ -1,20 +0,0 @@
|
||||
from subprocess import Popen, call, check_call, check_output, run
|
||||
|
||||
# Different Popen wrappers are checked.
|
||||
Popen("true", shell=False)
|
||||
call("true", shell=False)
|
||||
check_call("true", shell=False)
|
||||
check_output("true", shell=False)
|
||||
run("true", shell=False)
|
||||
|
||||
# Values that falsey values are treated as false.
|
||||
Popen("true", shell=0)
|
||||
Popen("true", shell=[])
|
||||
Popen("true", shell={})
|
||||
Popen("true", shell=None)
|
||||
|
||||
# Unknown values are treated as falsey.
|
||||
Popen("true", shell=True if True else False)
|
||||
|
||||
# No value is also caught.
|
||||
Popen("true")
|
||||
@@ -1,5 +0,0 @@
|
||||
def foo(shell):
|
||||
pass
|
||||
|
||||
|
||||
foo(shell=True)
|
||||
@@ -1,25 +0,0 @@
|
||||
import os
|
||||
|
||||
import commands
|
||||
import popen2
|
||||
|
||||
# Check all shell functions.
|
||||
os.system("true")
|
||||
os.popen("true")
|
||||
os.popen2("true")
|
||||
os.popen3("true")
|
||||
os.popen4("true")
|
||||
popen2.popen2("true")
|
||||
popen2.popen3("true")
|
||||
popen2.popen4("true")
|
||||
popen2.Popen3("true")
|
||||
popen2.Popen4("true")
|
||||
commands.getoutput("true")
|
||||
commands.getstatusoutput("true")
|
||||
|
||||
|
||||
# Check command argument looks unsafe.
|
||||
var_string = "true"
|
||||
os.system(var_string)
|
||||
os.system([var_string])
|
||||
os.system([var_string, ""])
|
||||
@@ -1,20 +0,0 @@
|
||||
import os
|
||||
|
||||
# Check all shell functions.
|
||||
os.execl("true")
|
||||
os.execle("true")
|
||||
os.execlp("true")
|
||||
os.execlpe("true")
|
||||
os.execv("true")
|
||||
os.execve("true")
|
||||
os.execvp("true")
|
||||
os.execvpe("true")
|
||||
os.spawnl("true")
|
||||
os.spawnle("true")
|
||||
os.spawnlp("true")
|
||||
os.spawnlpe("true")
|
||||
os.spawnv("true")
|
||||
os.spawnve("true")
|
||||
os.spawnvp("true")
|
||||
os.spawnvpe("true")
|
||||
os.startfile("true")
|
||||
@@ -1,44 +0,0 @@
|
||||
import os
|
||||
|
||||
# Check all functions.
|
||||
subprocess.Popen("true")
|
||||
subprocess.call("true")
|
||||
subprocess.check_call("true")
|
||||
subprocess.check_output("true")
|
||||
subprocess.run("true")
|
||||
os.system("true")
|
||||
os.popen("true")
|
||||
os.popen2("true")
|
||||
os.popen3("true")
|
||||
os.popen4("true")
|
||||
popen2.popen2("true")
|
||||
popen2.popen3("true")
|
||||
popen2.popen4("true")
|
||||
popen2.Popen3("true")
|
||||
popen2.Popen4("true")
|
||||
commands.getoutput("true")
|
||||
commands.getstatusoutput("true")
|
||||
os.execl("true")
|
||||
os.execle("true")
|
||||
os.execlp("true")
|
||||
os.execlpe("true")
|
||||
os.execv("true")
|
||||
os.execve("true")
|
||||
os.execvp("true")
|
||||
os.execvpe("true")
|
||||
os.spawnl("true")
|
||||
os.spawnle("true")
|
||||
os.spawnlp("true")
|
||||
os.spawnlpe("true")
|
||||
os.spawnv("true")
|
||||
os.spawnve("true")
|
||||
os.spawnvp("true")
|
||||
os.spawnvpe("true")
|
||||
os.startfile("true")
|
||||
|
||||
# Check it does not fail for full paths.
|
||||
os.system("/bin/ls")
|
||||
os.system("./bin/ls")
|
||||
os.system(["/bin/ls"])
|
||||
os.system(["/bin/ls", "/tmp"])
|
||||
os.system(r"C:\\bin\ls")
|
||||
@@ -1,9 +1,7 @@
|
||||
import collections
|
||||
import datetime as dt
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
import operator
|
||||
from pathlib import Path
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
@@ -167,26 +165,6 @@ def float_str_not_inf_or_nan_is_wrong(value=float("3.14")):
|
||||
pass
|
||||
|
||||
|
||||
# Allow decimals
|
||||
def decimal_okay(value=Decimal("0.1")):
|
||||
pass
|
||||
|
||||
# Allow dates
|
||||
def date_okay(value=dt.date(2023, 3, 27)):
|
||||
pass
|
||||
|
||||
# Allow datetimes
|
||||
def datetime_okay(value=dt.datetime(2023, 3, 27, 13, 51, 59)):
|
||||
pass
|
||||
|
||||
# Allow timedeltas
|
||||
def timedelta_okay(value=dt.timedelta(hours=1)):
|
||||
pass
|
||||
|
||||
# Allow paths
|
||||
def path_okay(value=Path(".")):
|
||||
pass
|
||||
|
||||
# B006 and B008
|
||||
# We should handle arbitrary nesting of these B008.
|
||||
def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"""
|
||||
Should emit:
|
||||
B017 - on lines 23 and 41
|
||||
B017 - on lines 20
|
||||
"""
|
||||
import asyncio
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
CONSTANT = True
|
||||
|
||||
@@ -35,14 +34,3 @@ class Foobar(unittest.TestCase):
|
||||
def raises_with_absolute_reference(self):
|
||||
with self.assertRaises(asyncio.CancelledError):
|
||||
Foo()
|
||||
|
||||
|
||||
def test_pytest_raises():
|
||||
with pytest.raises(Exception):
|
||||
raise ValueError("Hello")
|
||||
|
||||
with pytest.raises(Exception, "hello"):
|
||||
raise ValueError("This is fine")
|
||||
|
||||
with pytest.raises(Exception, match="hello"):
|
||||
raise ValueError("This is also fine")
|
||||
|
||||
@@ -57,9 +57,3 @@ def foo3():
|
||||
|
||||
def foo4():
|
||||
...
|
||||
|
||||
|
||||
def foo5():
|
||||
foo.bar # Attribute (raise)
|
||||
object().__class__ # Attribute (raise)
|
||||
"foo" + "bar" # BinOp (raise)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import warnings
|
||||
|
||||
"""
|
||||
Should emit:
|
||||
B028 - on lines 8 and 9
|
||||
"""
|
||||
|
||||
warnings.warn(DeprecationWarning("test"))
|
||||
warnings.warn(DeprecationWarning("test"), source=None)
|
||||
warnings.warn(DeprecationWarning("test"), source=None, stacklevel=2)
|
||||
warnings.warn(DeprecationWarning("test"), stacklevel=1)
|
||||
@@ -1,78 +0,0 @@
|
||||
"""
|
||||
Should emit:
|
||||
B030:
|
||||
- line 12, column 8
|
||||
- line 17, column 9
|
||||
- line 22, column 21
|
||||
- line 27, column 37
|
||||
"""
|
||||
|
||||
try:
|
||||
pass
|
||||
except 1: # error
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (1, ValueError): # error
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, (RuntimeError, (KeyError, TypeError))): # error
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, *(RuntimeError, (KeyError, TypeError))): # error
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(RuntimeError, (KeyError, TypeError))): # error
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, *(RuntimeError, TypeError)): # ok
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *b): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(RuntimeError, TypeError)): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(b, c)): # ok
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (*a, *(*b, *c)): # ok
|
||||
pass
|
||||
|
||||
|
||||
def what_to_catch():
|
||||
return ...
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except what_to_catch(): # ok
|
||||
pass
|
||||
@@ -1,187 +0,0 @@
|
||||
import itertools
|
||||
from itertools import groupby
|
||||
|
||||
shoppers = ["Jane", "Joe", "Sarah"]
|
||||
items = [
|
||||
("lettuce", "greens"),
|
||||
("tomatoes", "greens"),
|
||||
("cucumber", "greens"),
|
||||
("chicken breast", "meats & fish"),
|
||||
("salmon", "meats & fish"),
|
||||
("ice cream", "frozen items"),
|
||||
]
|
||||
|
||||
carts = {shopper: [] for shopper in shoppers}
|
||||
|
||||
|
||||
def collect_shop_items(shopper, items):
|
||||
# Imagine this an expensive database query or calculation that is
|
||||
# advantageous to batch.
|
||||
carts[shopper] += items
|
||||
|
||||
|
||||
# Invoking the `groupby` function directly
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
for shopper in shoppers:
|
||||
shopper = shopper.title()
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
# We're outside the nested loop and used the group again.
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
collect_shop_items("Jane", section_items)
|
||||
collect_shop_items("Joe", section_items) # B031
|
||||
|
||||
|
||||
# Make sure to detect in other loop constructs as well - `while` loop
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
countdown = 3
|
||||
while countdown > 0:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
countdown -= 1
|
||||
|
||||
# Make sure to detect in other loop constructs as well - `list` comprehension
|
||||
collection = []
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
collection.append([list(section_items) for _ in range(3)]) # B031
|
||||
|
||||
unique_items = set()
|
||||
another_set = set()
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# For nested loops, it should not flag the usage of the name
|
||||
for item in section_items:
|
||||
unique_items.add(item)
|
||||
|
||||
# But it should be detected when used again
|
||||
for item in section_items: # B031
|
||||
another_set.add(item)
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# Variable has been overridden, skip checking
|
||||
section_items = list(unique_items)
|
||||
collect_shop_items("Jane", section_items)
|
||||
collect_shop_items("Jane", section_items)
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# Variable has been overridden, skip checking
|
||||
# Not a realistic situation, just for testing purpose
|
||||
(section_items := list(unique_items))
|
||||
collect_shop_items("Jane", section_items)
|
||||
collect_shop_items("Jane", section_items)
|
||||
|
||||
for _section, section_items in groupby(items, key=lambda p: p[1]):
|
||||
# This is ok
|
||||
collect_shop_items("Jane", section_items)
|
||||
|
||||
# Invocation via the `itertools` module
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
for shopper in shoppers:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
_ = [collect_shop_items(shopper, section_items) for shopper in shoppers] # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# The variable is overridden, skip checking.
|
||||
_ = [_ for section_items in range(3)]
|
||||
_ = [collect_shop_items(shopper, section_items) for shopper in shoppers]
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
_ = [item for item in section_items]
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# The iterator is being used for the second time.
|
||||
_ = [(item1, item2) for item1 in section_items for item2 in section_items] # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
if _section == "greens":
|
||||
collect_shop_items(shopper, section_items)
|
||||
else:
|
||||
collect_shop_items(shopper, section_items)
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# Mutually exclusive branches shouldn't trigger the warning
|
||||
if _section == "greens":
|
||||
collect_shop_items(shopper, section_items)
|
||||
if _section == "greens":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
elif _section == "frozen items":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
else:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
elif _section == "frozen items":
|
||||
# Mix `match` and `if` statements
|
||||
match shopper:
|
||||
case "Jane":
|
||||
collect_shop_items(shopper, section_items)
|
||||
if _section == "fourth":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case _:
|
||||
collect_shop_items(shopper, section_items)
|
||||
else:
|
||||
collect_shop_items(shopper, section_items)
|
||||
# Now, it should detect
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
# Mutually exclusive branches shouldn't trigger the warning
|
||||
match _section:
|
||||
case "greens":
|
||||
collect_shop_items(shopper, section_items)
|
||||
match shopper:
|
||||
case "Jane":
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case _:
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case "frozen items":
|
||||
collect_shop_items(shopper, section_items)
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
case _:
|
||||
collect_shop_items(shopper, section_items)
|
||||
# Now, it should detect
|
||||
collect_shop_items(shopper, section_items) # B031
|
||||
|
||||
for group in groupby(items, key=lambda p: p[1]):
|
||||
# This is bad, but not detected currently
|
||||
collect_shop_items("Jane", group[1])
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
|
||||
# https://github.com/charliermarsh/ruff/issues/4050
|
||||
for _section, section_items in itertools.groupby(items, key=lambda p: p[1]):
|
||||
if _section == "greens":
|
||||
for item in section_items:
|
||||
collect_shop_items(shopper, item)
|
||||
elif _section == "frozen items":
|
||||
_ = [item for item in section_items]
|
||||
else:
|
||||
collect_shop_items(shopper, section_items)
|
||||
|
||||
# Make sure we ignore - but don't fail on more complicated invocations
|
||||
for _key, (_value1, _value2) in groupby(
|
||||
[("a", (1, 2)), ("b", (3, 4)), ("a", (5, 6))], key=lambda p: p[1]
|
||||
):
|
||||
collect_shop_items("Jane", group[1])
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
# Make sure we ignore - but don't fail on more complicated invocations
|
||||
for (_key1, _key2), (_value1, _value2) in groupby(
|
||||
[(("a", "a"), (1, 2)), (("b", "b"), (3, 4)), (("a", "a"), (5, 6))],
|
||||
key=lambda p: p[1],
|
||||
):
|
||||
collect_shop_items("Jane", group[1])
|
||||
collect_shop_items("Joe", group[1])
|
||||
|
||||
|
||||
# Let's redefine the `groupby` function to make sure we pick up the correct one.
|
||||
# NOTE: This should always be at the end of the file.
|
||||
def groupby(data, key=None):
|
||||
pass
|
||||
|
||||
|
||||
for name, group in groupby(items):
|
||||
collect_shop_items("Jane", items)
|
||||
# This shouldn't be flagged as the `groupby` function is different
|
||||
collect_shop_items("Joe", items)
|
||||
@@ -7,14 +7,11 @@ set(set(x))
|
||||
set(list(x))
|
||||
set(tuple(x))
|
||||
set(sorted(x))
|
||||
set(sorted(x, key=lambda y: y))
|
||||
set(reversed(x))
|
||||
sorted(list(x))
|
||||
sorted(tuple(x))
|
||||
sorted(sorted(x))
|
||||
sorted(sorted(x, key=lambda y: y))
|
||||
sorted(reversed(x))
|
||||
sorted(list(x), key=lambda y: y)
|
||||
tuple(
|
||||
list(
|
||||
[x, 3, "hell"\
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
x = [1, 2, 3]
|
||||
y = [("a", 1), ("b", 2), ("c", 3)]
|
||||
z = [(1,), (2,), (3,)]
|
||||
d = {"a": 1, "b": 2, "c": 3}
|
||||
|
||||
[i for i in x]
|
||||
{i for i in x}
|
||||
{k: v for k, v in y}
|
||||
{k: v for k, v in d.items()}
|
||||
|
||||
[i for i, in z]
|
||||
[i for i, j in y]
|
||||
[i for i in x if i > 1]
|
||||
[i for i in x for j in x]
|
||||
|
||||
{v: k for k, v in y}
|
||||
{k.foo: k for k in y}
|
||||
{k["foo"]: k for k in y}
|
||||
{k: v if v else None for k, v in y}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
dict({})
|
||||
dict({'a': 1})
|
||||
dict({'x': 1 for x in range(10)})
|
||||
dict(
|
||||
{'x': 1 for x in range(10)}
|
||||
)
|
||||
|
||||
dict({}, a=1)
|
||||
dict({x: 1 for x in range(1)}, a=1)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
any([x.id for x in bar])
|
||||
all([x.id for x in bar])
|
||||
any( # first comment
|
||||
[x.id for x in bar], # second comment
|
||||
) # third comment
|
||||
all( # first comment
|
||||
[x.id for x in bar], # second comment
|
||||
) # third comment
|
||||
any({x.id for x in bar})
|
||||
|
||||
# OK
|
||||
all(x.id for x in bar)
|
||||
all(x.id for x in bar)
|
||||
any(x.id for x in bar)
|
||||
all((x.id for x in bar))
|
||||
|
||||
|
||||
async def f() -> bool:
|
||||
return all([await use_greeting(greeting) for greeting in await greetings()])
|
||||
@@ -1,113 +0,0 @@
|
||||
from django.db import models
|
||||
from django.db.models import Model
|
||||
|
||||
|
||||
class StrBeforeRandomField(models.Model):
|
||||
"""Model with `__str__` before a random property."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return ""
|
||||
|
||||
random_property = "foo"
|
||||
|
||||
|
||||
class StrBeforeFieldModel(models.Model):
|
||||
"""Model with `__str__` before fields."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return "foobar"
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
|
||||
|
||||
class ManagerBeforeField(models.Model):
|
||||
"""Model with manager before fields."""
|
||||
|
||||
objects = "manager"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return "foobar"
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
|
||||
|
||||
class CustomMethodBeforeStr(models.Model):
|
||||
"""Model with a custom method before `__str__`."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def my_method(self):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return "foobar"
|
||||
|
||||
|
||||
class GetAbsoluteUrlBeforeSave(Model):
|
||||
"""Model with `get_absolute_url` method before `save` method.
|
||||
|
||||
Subclass this directly using the `Model` class.
|
||||
"""
|
||||
|
||||
def get_absolute_url(self):
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
|
||||
class ConstantsAreNotFields(models.Model):
|
||||
"""Model with an assignment to a constant after `__str__`."""
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
pass
|
||||
|
||||
MY_CONSTANT = id(1)
|
||||
|
||||
|
||||
class PerfectlyFine(models.Model):
|
||||
"""Model which has everything in perfect order."""
|
||||
|
||||
first_name = models.CharField(max_length=32)
|
||||
last_name = models.CharField(max_length=32)
|
||||
objects = "manager"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "test"
|
||||
verbose_name_plural = "tests"
|
||||
|
||||
def __str__(self):
|
||||
return "Perfectly fine!"
|
||||
|
||||
def save(self, **kwargs):
|
||||
super(PerfectlyFine, self).save(**kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return "http://%s" % self
|
||||
|
||||
def my_method(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def random_property(self):
|
||||
return "%s" % self
|
||||
@@ -1 +0,0 @@
|
||||
_(f"{'value'}")
|
||||
@@ -1 +0,0 @@
|
||||
_("{}".format("line"))
|
||||
@@ -1 +0,0 @@
|
||||
_("%s" % "line")
|
||||
@@ -1,16 +0,0 @@
|
||||
import typing as t # banned
|
||||
import typing as ty # banned
|
||||
|
||||
import numpy as nmp # banned
|
||||
import numpy as npy # banned
|
||||
import tensorflow.keras.backend as K # banned
|
||||
import torch.nn.functional as F # banned
|
||||
from tensorflow.keras import backend as K # banned
|
||||
from torch.nn import functional as F # banned
|
||||
|
||||
from typing import Any # ok
|
||||
|
||||
import numpy as np # ok
|
||||
import tensorflow as tf # ok
|
||||
import torch.nn as nn # ok
|
||||
from tensorflow.keras import backend # ok
|
||||
@@ -1,10 +0,0 @@
|
||||
from logging.config import BaseConfigurator # banned
|
||||
from typing import Any, Dict # banned
|
||||
from typing import * # banned
|
||||
|
||||
from pandas import DataFrame # banned
|
||||
from pandas import * # banned
|
||||
|
||||
import logging.config # ok
|
||||
import typing # ok
|
||||
import pandas # ok
|
||||
@@ -1,9 +1,3 @@
|
||||
import logging
|
||||
import logging as foo
|
||||
|
||||
logging.info("Hello {}".format("World!"))
|
||||
logging.log(logging.INFO, "Hello {}".format("World!"))
|
||||
foo.info("Hello {}".format("World!"))
|
||||
logging.log(logging.INFO, msg="Hello {}".format("World!"))
|
||||
logging.log(level=logging.INFO, msg="Hello {}".format("World!"))
|
||||
logging.log(msg="Hello {}".format("World!"), level=logging.INFO)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello %s" % "World!")
|
||||
logging.log(logging.INFO, "Hello %s" % "World!")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import logging
|
||||
|
||||
logging.info("Hello" + " " + "World!")
|
||||
logging.log(logging.INFO, "Hello" + " " + "World!")
|
||||
|
||||
@@ -2,4 +2,3 @@ import logging
|
||||
|
||||
name = "world"
|
||||
logging.info(f"Hello {name}")
|
||||
logging.log(logging.INFO, f"Hello {name}")
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import logging
|
||||
from distutils import log
|
||||
|
||||
logging.warn("Hello World!")
|
||||
log.warn("Hello world!") # This shouldn't be considered as a logger candidate
|
||||
|
||||
10
crates/ruff/resources/test/fixtures/flake8_pie/PIE802.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_pie/PIE802.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# no error
|
||||
all((x.id for x in bar))
|
||||
all(x.id for x in bar)
|
||||
all(x.id for x in bar)
|
||||
any(x.id for x in bar)
|
||||
any({x.id for x in bar})
|
||||
|
||||
# PIE 802
|
||||
any([x.id for x in bar])
|
||||
all([x.id for x in bar])
|
||||
@@ -6,8 +6,6 @@ obj.endswith("foo") or obj.endswith("bar")
|
||||
obj.startswith(foo) or obj.startswith(bar)
|
||||
# error
|
||||
obj.startswith(foo) or obj.startswith("foo")
|
||||
# error
|
||||
obj.endswith(foo) or obj.startswith(foo) or obj.startswith("foo")
|
||||
|
||||
# ok
|
||||
obj.startswith(("foo", "bar"))
|
||||
|
||||
@@ -11,7 +11,3 @@ _T = TypeVar("_T") # OK
|
||||
_TTuple = TypeVarTuple("_TTuple") # OK
|
||||
|
||||
_P = ParamSpec("_P") # OK
|
||||
|
||||
|
||||
def f():
|
||||
T = TypeVar("T") # OK
|
||||
|
||||
@@ -11,6 +11,3 @@ _T = TypeVar("_T") # OK
|
||||
_TTuple = TypeVarTuple("_TTuple") # OK
|
||||
|
||||
_P = ParamSpec("_P") # OK
|
||||
|
||||
def f():
|
||||
T = TypeVar("T") # OK
|
||||
|
||||
@@ -1,161 +1,79 @@
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
from math import inf
|
||||
|
||||
import numpy as np
|
||||
|
||||
def f12(
|
||||
x,
|
||||
y: str = os.pathsep, # OK
|
||||
) -> None: ...
|
||||
def f11(*, x: str = "x") -> None: ... # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f11(*, x: str = "x") -> None: # OK
|
||||
...
|
||||
|
||||
|
||||
def f13(
|
||||
x: list[str] = [ # OK
|
||||
x: list[str] = [
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
]
|
||||
) -> None: ...
|
||||
] # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f14(
|
||||
x: tuple[str, ...] = ( # OK
|
||||
x: tuple[str, ...] = (
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
)
|
||||
) -> None: ...
|
||||
) # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f15(
|
||||
x: set[str] = { # OK
|
||||
x: set[str] = {
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
}
|
||||
) -> None: ...
|
||||
def f151(x: dict[int, int] = {1: 2}) -> None: ... # Ok
|
||||
def f152(
|
||||
x: dict[
|
||||
int, int
|
||||
] = { # OK
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None: ...
|
||||
def f153(
|
||||
x: list[
|
||||
int
|
||||
] = [ # OK
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None: ...
|
||||
def f154(
|
||||
x: tuple[
|
||||
str, tuple[str, ...]
|
||||
] = ( # OK
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None: ...
|
||||
def f141(
|
||||
x: list[
|
||||
int
|
||||
] = [ # OK
|
||||
*range(10)
|
||||
],
|
||||
) -> None: ...
|
||||
def f142(
|
||||
x: list[
|
||||
int
|
||||
] = list( # OK
|
||||
range(10)
|
||||
),
|
||||
) -> None: ...
|
||||
def f16(
|
||||
x: frozenset[
|
||||
bytes
|
||||
] = frozenset( # OK
|
||||
{b"foo", b"bar", b"baz"}
|
||||
)
|
||||
) -> None: ...
|
||||
} # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f16(x: frozenset[bytes] = frozenset({b"foo", b"bar", b"baz"})) -> None: # OK
|
||||
...
|
||||
|
||||
|
||||
def f17(
|
||||
x: str = "foo" # OK
|
||||
+ "bar",
|
||||
) -> None: ...
|
||||
x: str = "foo" + "bar", # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f18(
|
||||
x: str = b"foo" # OK
|
||||
+ b"bar",
|
||||
) -> None: ...
|
||||
x: str = b"foo" + b"bar", # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f19(
|
||||
x: object = "foo" # OK
|
||||
+ 4,
|
||||
) -> None: ...
|
||||
x: object = "foo" + 4, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f20(
|
||||
x: int = 5
|
||||
+ 5, # OK
|
||||
) -> None: ...
|
||||
x: int = 5 + 5, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f21(
|
||||
x: complex = 3j
|
||||
- 3j, # OK
|
||||
) -> None: ...
|
||||
x: complex = 3j - 3j, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f22(
|
||||
x: complex = -42.5j # OK
|
||||
+ 4.3j,
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x: bool = True, # OK
|
||||
) -> None: ...
|
||||
def f24(
|
||||
x: float = 3.14, # OK
|
||||
) -> None: ...
|
||||
def f25(
|
||||
x: float = -3.14, # OK
|
||||
) -> None: ...
|
||||
def f26(
|
||||
x: complex = -3.14j, # OK
|
||||
) -> None: ...
|
||||
def f27(
|
||||
x: complex = -3 - 3.14j, # OK
|
||||
) -> None: ...
|
||||
def f28(
|
||||
x: float = math.tau, # OK
|
||||
) -> None: ...
|
||||
def f29(
|
||||
x: float = math.inf, # OK
|
||||
) -> None: ...
|
||||
def f30(
|
||||
x: float = -math.inf, # OK
|
||||
) -> None: ...
|
||||
def f31(
|
||||
x: float = inf, # OK
|
||||
) -> None: ...
|
||||
def f32(
|
||||
x: float = np.inf, # OK
|
||||
) -> None: ...
|
||||
def f33(
|
||||
x: float = math.nan, # OK
|
||||
) -> None: ...
|
||||
def f34(
|
||||
x: float = -math.nan, # OK
|
||||
) -> None: ...
|
||||
def f35(
|
||||
x: complex = math.inf # OK
|
||||
+ 1j,
|
||||
) -> None: ...
|
||||
def f36(
|
||||
*,
|
||||
x: str = sys.version, # OK
|
||||
) -> None: ...
|
||||
def f37(
|
||||
*,
|
||||
x: str = "" # OK
|
||||
+ "",
|
||||
) -> None: ...
|
||||
x: complex = -42.5j + 4.3j, # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
@@ -1,84 +1,35 @@
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
from math import inf
|
||||
|
||||
import numpy as np
|
||||
|
||||
def f12(
|
||||
x,
|
||||
y: str = os.pathsep, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f11(*, x: str = "x") -> None: ... # OK
|
||||
def f13(
|
||||
x: list[str] = [ # OK
|
||||
x: list[
|
||||
str
|
||||
] = [ # Error PYI011 Only simple default values allowed for typed arguments
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
]
|
||||
) -> None: ...
|
||||
def f14(
|
||||
x: tuple[str, ...] = ( # OK
|
||||
x: tuple[
|
||||
str, ...
|
||||
] = ( # Error PYI011 Only simple default values allowed for typed arguments
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
)
|
||||
) -> None: ...
|
||||
def f15(
|
||||
x: set[str] = { # OK
|
||||
x: set[
|
||||
str
|
||||
] = { # Error PYI011 Only simple default values allowed for typed arguments
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
}
|
||||
) -> None: ...
|
||||
def f151(x: dict[int, int] = {1: 2}) -> None: ... # Ok
|
||||
def f152(
|
||||
x: dict[
|
||||
int, int
|
||||
] = { # Error PYI011 Only simple default values allowed for typed arguments
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None: ...
|
||||
def f153(
|
||||
x: list[
|
||||
int
|
||||
] = [ # Error PYI011 Only simple default values allowed for typed arguments
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None: ...
|
||||
def f154(
|
||||
x: tuple[
|
||||
str, tuple[str, ...]
|
||||
] = ( # Error PYI011 Only simple default values allowed for typed arguments
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None: ...
|
||||
def f141(
|
||||
x: list[
|
||||
int
|
||||
] = [ # Error PYI011 Only simple default values allowed for typed arguments
|
||||
*range(10)
|
||||
],
|
||||
) -> None: ...
|
||||
def f142(
|
||||
x: list[
|
||||
int
|
||||
] = list( # Error PYI011 Only simple default values allowed for typed arguments
|
||||
range(10)
|
||||
),
|
||||
) -> None: ...
|
||||
def f16(
|
||||
x: frozenset[
|
||||
bytes
|
||||
@@ -110,52 +61,3 @@ def f22(
|
||||
x: complex = -42.5j # Error PYI011 Only simple default values allowed for typed arguments
|
||||
+ 4.3j,
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x: bool = True, # OK
|
||||
) -> None: ...
|
||||
def f24(
|
||||
x: float = 3.14, # OK
|
||||
) -> None: ...
|
||||
def f25(
|
||||
x: float = -3.14, # OK
|
||||
) -> None: ...
|
||||
def f26(
|
||||
x: complex = -3.14j, # OK
|
||||
) -> None: ...
|
||||
def f27(
|
||||
x: complex = -3 - 3.14j, # OK
|
||||
) -> None: ...
|
||||
def f28(
|
||||
x: float = math.tau, # OK
|
||||
) -> None: ...
|
||||
def f29(
|
||||
x: float = math.inf, # OK
|
||||
) -> None: ...
|
||||
def f30(
|
||||
x: float = -math.inf, # OK
|
||||
) -> None: ...
|
||||
def f31(
|
||||
x: float = inf, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f32(
|
||||
x: float = np.inf, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f33(
|
||||
x: float = math.nan, # OK
|
||||
) -> None: ...
|
||||
def f34(
|
||||
x: float = -math.nan, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f35(
|
||||
x: complex = math.inf # Error PYI011 Only simple default values allowed for typed arguments
|
||||
+ 1j,
|
||||
) -> None: ...
|
||||
def f36(
|
||||
*,
|
||||
x: str = sys.version, # OK
|
||||
) -> None: ...
|
||||
def f37(
|
||||
*,
|
||||
x: str = "" # Error PYI011 Only simple default values allowed for typed arguments
|
||||
+ "",
|
||||
) -> None: ...
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
# Violations of PYI012
|
||||
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class OneAttributeClassRev:
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains pass.
|
||||
"""
|
||||
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
# Not violations (of PYI012)
|
||||
|
||||
|
||||
class EmptyClass:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
|
||||
class EmptyOneLine:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
|
||||
class EmptyEllipsis:
|
||||
...
|
||||
|
||||
|
||||
class NonEmptyEllipsis:
|
||||
value: int
|
||||
... # Y013 Non-empty class body must not contain `...`
|
||||
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
|
||||
pass
|
||||
@@ -1,59 +0,0 @@
|
||||
# Violations of PYI012
|
||||
|
||||
class OneAttributeClass:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class OneAttributeClassRev:
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
class DocstringClass:
|
||||
"""
|
||||
My body only contains pass.
|
||||
"""
|
||||
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class NonEmptyChild(Exception):
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
class NonEmptyChild2(Exception):
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
value: int
|
||||
|
||||
class NonEmptyWithInit:
|
||||
value: int
|
||||
pass # PYI012 Class body must not contain `pass`
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
# Not violations (of PYI012)
|
||||
|
||||
class EmptyClass:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
class EmptyOneLine:
|
||||
pass # Y009 Empty body should contain `...`, not `pass`
|
||||
|
||||
class Dog:
|
||||
eyes: int = 2
|
||||
|
||||
class EmptyEllipsis: ...
|
||||
|
||||
class NonEmptyEllipsis:
|
||||
value: int
|
||||
... # Y013 Non-empty class body must not contain `...`
|
||||
|
||||
class WithInit:
|
||||
value: int = 0
|
||||
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
def function():
|
||||
pass
|
||||
|
||||
pass
|
||||
@@ -39,58 +39,6 @@ def f15(
|
||||
...
|
||||
|
||||
|
||||
def f151(x={1: 2}) -> None: # Ok
|
||||
...
|
||||
|
||||
|
||||
def f152(
|
||||
x={ # OK
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f153(
|
||||
x=[ # OK
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f154(
|
||||
x=( # OK
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f141(
|
||||
x=[*range(10)], # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f142(
|
||||
x=list(range(10)), # OK
|
||||
) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f16(x=frozenset({b"foo", b"bar", b"baz"})) -> None:
|
||||
... # OK
|
||||
|
||||
|
||||
@@ -4,60 +4,26 @@ def f12(
|
||||
) -> None: ...
|
||||
def f11(*, x="x") -> None: ... # OK
|
||||
def f13(
|
||||
x=[ # OK
|
||||
x=[ # Error PYI014
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
]
|
||||
) -> None: ...
|
||||
def f14(
|
||||
x=( # OK
|
||||
x=( # Error PYI014
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
)
|
||||
) -> None: ...
|
||||
def f15(
|
||||
x={ # OK
|
||||
x={ # Error PYI014
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
}
|
||||
) -> None: ...
|
||||
def f151(x={1: 2}) -> None: ...
|
||||
def f152(
|
||||
x={ # Error PYI014
|
||||
1: 2,
|
||||
**{3: 4},
|
||||
}
|
||||
) -> None: ...
|
||||
def f153(
|
||||
x=[ # Error PYI014
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
]
|
||||
) -> None: ...
|
||||
def f154(
|
||||
x=( # Error PYI014
|
||||
"foo",
|
||||
("bar", "baz"),
|
||||
)
|
||||
) -> None: ...
|
||||
def f141(
|
||||
x=[*range(10)], # Error PYI014
|
||||
) -> None: ...
|
||||
def f142(
|
||||
x=list(range(10)), # Error PYI014
|
||||
) -> None: ...
|
||||
def f16(x=frozenset({b"foo", b"bar", b"baz"})) -> None: ... # Error PYI014
|
||||
def f17(
|
||||
x="foo" + "bar", # Error PYI014
|
||||
@@ -77,6 +43,3 @@ def f21(
|
||||
def f22(
|
||||
x=-42.5j + 4.3j, # Error PYI014
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x=True, # OK
|
||||
) -> None: ...
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final
|
||||
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
@@ -1,100 +0,0 @@
|
||||
import builtins
|
||||
import typing
|
||||
from typing import TypeAlias, Final, NewType, TypeVar, TypeVarTuple, ParamSpec
|
||||
|
||||
# We shouldn't emit Y015 for simple default values
|
||||
field1: int
|
||||
field2: int = ...
|
||||
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
field4: int = 0
|
||||
field41: int = 0xFFFFFFFF
|
||||
field42: int = 1234567890
|
||||
field43: int = -0xFFFFFFFF
|
||||
field44: int = -1234567890
|
||||
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
|
||||
field6 = 0 # Y052 Need type annotation for "field6"
|
||||
field7 = b"" # Y052 Need type annotation for "field7"
|
||||
field71 = "foo" # Y052 Need type annotation for "field71"
|
||||
field72: str = "foo"
|
||||
field8 = False # Y052 Need type annotation for "field8"
|
||||
field81 = -1 # Y052 Need type annotation for "field81"
|
||||
field82: float = -98.43
|
||||
field83 = -42j # Y052 Need type annotation for "field83"
|
||||
field84 = 5 + 42j # Y052 Need type annotation for "field84"
|
||||
field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
||||
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
||||
Field95: TypeAlias = None
|
||||
Field96: TypeAlias = int | None
|
||||
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
|
||||
Field98 = NewType('MyInt', int)
|
||||
Field99 = TypeVar('Field99')
|
||||
Field100 = TypeVarTuple('Field100')
|
||||
Field101 = ParamSpec('Field101')
|
||||
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
|
||||
field191: list[int] = [1, 2, 3]
|
||||
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
||||
field201: tuple[int, ...] = (1, 2, 3)
|
||||
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
||||
field211: set[int] = {1, 2, 3}
|
||||
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
||||
field213: dict[str, str] = {"foo": "bar"}
|
||||
field22: Final = {"foo": 5}
|
||||
|
||||
# We *should* emit Y015 for more complex default values
|
||||
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
|
||||
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
|
||||
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
|
||||
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
|
||||
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
|
||||
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
|
||||
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
|
||||
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
|
||||
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
|
||||
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
|
||||
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
|
||||
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
|
||||
|
||||
# We shouldn't emit Y015 within functions
|
||||
def f():
|
||||
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
|
||||
# We shouldn't emit Y015 for __slots__ or __match_args__
|
||||
class Class1:
|
||||
__slots__ = (
|
||||
'_one',
|
||||
'_two',
|
||||
'_three',
|
||||
'_four',
|
||||
'_five',
|
||||
'_six',
|
||||
'_seven',
|
||||
'_eight',
|
||||
'_nine',
|
||||
'_ten',
|
||||
'_eleven',
|
||||
)
|
||||
|
||||
__match_args__ = (
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
'four',
|
||||
'five',
|
||||
'six',
|
||||
'seven',
|
||||
'eight',
|
||||
'nine',
|
||||
'ten',
|
||||
'eleven',
|
||||
)
|
||||
|
||||
# We shouldn't emit Y015 for __all__
|
||||
__all__ = ["Class1"]
|
||||
|
||||
# Ignore the following for PYI015
|
||||
field26 = typing.Sequence[int]
|
||||
field27 = list[str]
|
||||
field28 = builtins.str
|
||||
field29 = str
|
||||
field30 = str | bytes | None
|
||||
@@ -1,35 +0,0 @@
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: str | str # PYI016: Duplicate union member `str`
|
||||
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: int | int): # PYI016: Duplicate union member `int`
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> str | str: # PYI016: Duplicate union member `str`
|
||||
return "my string"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: str | str | int # PYI016: Duplicate union member `str`
|
||||
field4: int | int | str # PYI016: Duplicate union member `int`
|
||||
field5: str | int | str # PYI016: Duplicate union member `str`
|
||||
field6: int | bool | str | int # PYI016: Duplicate union member `int`
|
||||
|
||||
# Shouldn't emit for non-type unions.
|
||||
field7 = str | str
|
||||
|
||||
# Should emit for strangely-bracketed unions.
|
||||
field8: int | (str | int) # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should handle user brackets when fixing.
|
||||
field9: int | (int | str) # PYI016: Duplicate union member `int`
|
||||
field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
@@ -1,32 +0,0 @@
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: str | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: int | int): # PYI016: Duplicate union member `int`
|
||||
print(arg1)
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> str | str: # PYI016: Duplicate union member `str`
|
||||
return "my string"
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: str | str | int # PYI016: Duplicate union member `str`
|
||||
field4: int | int | str # PYI016: Duplicate union member `int`
|
||||
field5: str | int | str # PYI016: Duplicate union member `str`
|
||||
field6: int | bool | str | int # PYI016: Duplicate union member `int`
|
||||
|
||||
# Shouldn't emit for non-type unions.
|
||||
field7 = str | str
|
||||
|
||||
# Should emit for strangely-bracketed unions.
|
||||
field8: int | (str | int) # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should handle user brackets when fixing.
|
||||
field9: int | (int | str) # PYI016: Duplicate union member `int`
|
||||
field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
@@ -1,33 +0,0 @@
|
||||
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import TypeAlias
|
||||
|
||||
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
def func(
|
||||
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
): ...
|
||||
|
||||
class Foo:
|
||||
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
G: TypeAlias = None # type: ignore
|
||||
H: TypeAlias = None # type: ignore[attr-defined]
|
||||
I: TypeAlias = None #type: ignore
|
||||
J: TypeAlias = None # type: ignore
|
||||
K: TypeAlias = None# type: ignore
|
||||
L: TypeAlias = None#type:ignore
|
||||
|
||||
# Whole line commented out # type: int
|
||||
M: TypeAlias = None # type: can't parse me!
|
||||
|
||||
class Bar:
|
||||
N: TypeAlias = None # type: can't parse me either!
|
||||
# This whole line is commented out and indented # type: str
|
||||
@@ -1,33 +0,0 @@
|
||||
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import TypeAlias
|
||||
|
||||
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
def func(
|
||||
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
): ...
|
||||
|
||||
class Foo:
|
||||
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
|
||||
|
||||
G: TypeAlias = None # type: ignore
|
||||
H: TypeAlias = None # type: ignore[attr-defined]
|
||||
I: TypeAlias = None #type: ignore
|
||||
J: TypeAlias = None # type: ignore
|
||||
K: TypeAlias = None# type: ignore
|
||||
L: TypeAlias = None#type:ignore
|
||||
|
||||
# Whole line commented out # type: int
|
||||
M: TypeAlias = None # type: can't parse me!
|
||||
|
||||
class Bar:
|
||||
N: TypeAlias = None # type: can't parse me either!
|
||||
# This whole line is commented out and indented # type: str
|
||||
@@ -49,18 +49,3 @@ def test_list_expressions(param1, param2):
|
||||
@pytest.mark.parametrize([some_expr, "param2"], [1, 2, 3])
|
||||
def test_list_mixed_expr_literal(param1, param2):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("param1, " "param2, " "param3"), [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_with_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize("param1, " "param2, " "param3", [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_no_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize((("param1, " "param2, " "param3")), [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_with_multi_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
@@ -2,13 +2,3 @@ def x(y):
|
||||
if not y:
|
||||
return
|
||||
return None # error
|
||||
|
||||
|
||||
class BaseCache:
|
||||
def get(self, key: str) -> str | None:
|
||||
print(f"{key} not found")
|
||||
return None
|
||||
|
||||
def get(self, key: str) -> None:
|
||||
print(f"{key} not found")
|
||||
return None
|
||||
|
||||
@@ -293,30 +293,3 @@ def x(y):
|
||||
|
||||
def foo(baz: str) -> str:
|
||||
return baz
|
||||
|
||||
|
||||
def end_of_statement():
|
||||
def example():
|
||||
if True:
|
||||
return ""
|
||||
|
||||
|
||||
def example():
|
||||
if True:
|
||||
return ""
|
||||
|
||||
|
||||
def example():
|
||||
if True:
|
||||
return "" # type: ignore
|
||||
|
||||
|
||||
def example():
|
||||
if True:
|
||||
return "" ;
|
||||
|
||||
|
||||
def example():
|
||||
if True:
|
||||
return "" \
|
||||
; # type: ignore
|
||||
|
||||
@@ -3,7 +3,23 @@
|
||||
###
|
||||
def x():
|
||||
a = 1
|
||||
return a # RET504
|
||||
return a # error
|
||||
|
||||
|
||||
def x():
|
||||
a = 1
|
||||
print(a)
|
||||
a = 2
|
||||
return a # error
|
||||
|
||||
|
||||
def x():
|
||||
a = 1
|
||||
if True:
|
||||
return a # error
|
||||
a = 2
|
||||
print(a)
|
||||
return a
|
||||
|
||||
|
||||
# Can be refactored false positives
|
||||
@@ -149,6 +165,44 @@ def my_func():
|
||||
return foo
|
||||
|
||||
|
||||
# Refactored incorrect false positives
|
||||
# See test cases above marked: "Can be refactored false positives"
|
||||
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
|
||||
def get_bar_if_exists(obj):
|
||||
if hasattr(obj, "bar"):
|
||||
return str(obj.bar)
|
||||
return ""
|
||||
|
||||
|
||||
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
|
||||
def x():
|
||||
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
|
||||
# clean up after any blank components
|
||||
return formatted.replace("()", "").replace(" ", " ").strip()
|
||||
|
||||
|
||||
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
|
||||
def user_agent_username(username=None):
|
||||
|
||||
if not username:
|
||||
return ""
|
||||
|
||||
username = username.replace(" ", "_") # Avoid spaces or %20.
|
||||
try:
|
||||
username.encode("ascii") # just test,
|
||||
# but not actually use it
|
||||
except UnicodeEncodeError:
|
||||
username = quote(username.encode("utf-8"))
|
||||
else:
|
||||
# % is legal in the default $wgLegalTitleChars
|
||||
# This is so that ops know the real pywikibot will not
|
||||
# allow a useragent in the username to allow through a
|
||||
# hand-coded percent-encoded value.
|
||||
if "%" in username:
|
||||
username = quote(username)
|
||||
return username
|
||||
|
||||
|
||||
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
|
||||
def no_exception_loop():
|
||||
success = False
|
||||
@@ -206,69 +260,3 @@ def nonlocal_assignment():
|
||||
nonlocal X
|
||||
X = 1
|
||||
return X
|
||||
|
||||
|
||||
def decorator() -> Flask:
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/hello")
|
||||
def hello() -> str:
|
||||
"""Hello endpoint."""
|
||||
return "Hello, World!"
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def default():
|
||||
y = 1
|
||||
|
||||
def f(x=y) -> X:
|
||||
return x
|
||||
|
||||
return y
|
||||
|
||||
|
||||
# Multiple assignment
|
||||
def get_queryset(option_1, option_2):
|
||||
queryset: Any = None
|
||||
queryset = queryset.filter(a=1)
|
||||
if option_1:
|
||||
queryset = queryset.annotate(b=Value(2))
|
||||
if option_2:
|
||||
queryset = queryset.filter(c=3)
|
||||
return queryset
|
||||
|
||||
|
||||
def get_queryset():
|
||||
queryset = Model.filter(a=1)
|
||||
queryset = queryset.filter(c=3)
|
||||
return queryset
|
||||
|
||||
|
||||
def get_queryset():
|
||||
queryset = Model.filter(a=1)
|
||||
return queryset # RET504
|
||||
|
||||
|
||||
# Function arguments
|
||||
def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return val
|
||||
val = val.strip().lower()
|
||||
if val in ("1", "true", "yes"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return val
|
||||
val = 1
|
||||
return val # RET504
|
||||
|
||||
|
||||
def str_to_bool(val):
|
||||
if isinstance(val, bool):
|
||||
return some_obj
|
||||
return val
|
||||
|
||||
@@ -16,26 +16,15 @@ if isinstance(a, int) or isinstance(b, bool) or isinstance(a, float): # SIM101
|
||||
if (isinstance(a, int) or isinstance(a, float)) and isinstance(b, bool): # SIM101
|
||||
pass
|
||||
|
||||
if isinstance(a.b, int) or isinstance(a.b, float): # SIM101
|
||||
pass
|
||||
|
||||
if isinstance(a(), int) or isinstance(a(), float): # SIM101
|
||||
pass
|
||||
|
||||
if isinstance(a, int) and isinstance(b, bool) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
if isinstance(a, bool) or isinstance(b, str):
|
||||
pass
|
||||
|
||||
if isinstance(a, int) or isinstance(a.b, float):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def isinstance(a, b):
|
||||
return False
|
||||
|
||||
if isinstance(a, int) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
@@ -46,10 +46,10 @@ if a:
|
||||
if b:
|
||||
c
|
||||
|
||||
while x > 0:
|
||||
while True:
|
||||
# SIM102
|
||||
if y > 0:
|
||||
if z > 0:
|
||||
if True:
|
||||
if True:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
@@ -64,8 +64,8 @@ is valid"""
|
||||
|
||||
|
||||
# SIM102
|
||||
if x > 0:
|
||||
if y > 0:
|
||||
if True:
|
||||
if True:
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
@@ -78,7 +78,7 @@ is valid"""
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
while x > 0:
|
||||
while True:
|
||||
# SIM102
|
||||
if node.module:
|
||||
if node.module == "multiprocessing" or node.module.startswith(
|
||||
@@ -129,15 +129,3 @@ if a:
|
||||
print("baz")
|
||||
else:
|
||||
print("bar")
|
||||
|
||||
|
||||
# OK
|
||||
if False:
|
||||
if a:
|
||||
pass
|
||||
|
||||
|
||||
# OK
|
||||
if True:
|
||||
if a:
|
||||
pass
|
||||
|
||||
@@ -59,15 +59,3 @@ def bar():
|
||||
return foo()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def with_ellipsis():
|
||||
try:
|
||||
foo()
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
def with_ellipsis_and_return():
|
||||
try:
|
||||
return foo()
|
||||
except ValueError:
|
||||
...
|
||||
|
||||
@@ -9,17 +9,6 @@ os.environ.get('foo', 'bar')
|
||||
|
||||
os.getenv('foo')
|
||||
|
||||
env = os.environ.get('foo')
|
||||
|
||||
env = os.environ['foo']
|
||||
|
||||
if env := os.environ.get('foo'):
|
||||
pass
|
||||
|
||||
if env := os.environ['foo']:
|
||||
pass
|
||||
|
||||
|
||||
# Good
|
||||
os.environ['FOO']
|
||||
|
||||
@@ -28,13 +17,3 @@ os.environ.get('FOO')
|
||||
os.environ.get('FOO', 'bar')
|
||||
|
||||
os.getenv('FOO')
|
||||
|
||||
env = os.getenv('FOO')
|
||||
|
||||
if env := os.getenv('FOO'):
|
||||
pass
|
||||
|
||||
env = os.environ['FOO']
|
||||
|
||||
if env := os.environ['FOO']:
|
||||
pass
|
||||
|
||||
@@ -20,5 +20,3 @@ for key in list(obj.keys()):
|
||||
{k: k for k in obj.keys()} # SIM118
|
||||
|
||||
(k for k in obj.keys()) # SIM118
|
||||
|
||||
key in (obj or {}).keys() # SIM118
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
if a or True: # SIM222
|
||||
if a or True: # SIM223
|
||||
pass
|
||||
|
||||
if (a or b) or True: # SIM222
|
||||
if (a or b) or True: # SIM223
|
||||
pass
|
||||
|
||||
if a or (b or True): # SIM222
|
||||
if a or (b or True): # SIM223
|
||||
pass
|
||||
|
||||
if a and True: # OK
|
||||
@@ -16,139 +16,3 @@ if True: # OK
|
||||
|
||||
def validate(self, value):
|
||||
return json.loads(value) or True # OK
|
||||
|
||||
|
||||
if a or f() or b or g() or True: # OK
|
||||
pass
|
||||
|
||||
if a or f() or True or g() or b: # SIM222
|
||||
pass
|
||||
|
||||
if True or f() or a or g() or b: # SIM222
|
||||
pass
|
||||
|
||||
if a or True or f() or b or g(): # SIM222
|
||||
pass
|
||||
|
||||
|
||||
if a and f() and b and g() and False: # OK
|
||||
pass
|
||||
|
||||
if a and f() and False and g() and b: # OK
|
||||
pass
|
||||
|
||||
if False and f() and a and g() and b: # OK
|
||||
pass
|
||||
|
||||
if a and False and f() and b and g(): # OK
|
||||
pass
|
||||
|
||||
|
||||
a or "" or True # SIM222
|
||||
|
||||
a or "foo" or True or "bar" # SIM222
|
||||
|
||||
a or 0 or True # SIM222
|
||||
|
||||
a or 1 or True or 2 # SIM222
|
||||
|
||||
a or 0.0 or True # SIM222
|
||||
|
||||
a or 0.1 or True or 0.2 # SIM222
|
||||
|
||||
a or [] or True # SIM222
|
||||
|
||||
a or list([]) or True # SIM222
|
||||
|
||||
a or [1] or True or [2] # SIM222
|
||||
|
||||
a or list([1]) or True or list([2]) # SIM222
|
||||
|
||||
a or {} or True # SIM222
|
||||
|
||||
a or dict() or True # SIM222
|
||||
|
||||
a or {1: 1} or True or {2: 2} # SIM222
|
||||
|
||||
a or dict({1: 1}) or True or dict({2: 2}) # SIM222
|
||||
|
||||
a or set() or True # SIM222
|
||||
|
||||
a or set(set()) or True # SIM222
|
||||
|
||||
a or {1} or True or {2} # SIM222
|
||||
|
||||
a or set({1}) or True or set({2}) # SIM222
|
||||
|
||||
a or () or True # SIM222
|
||||
|
||||
a or tuple(()) or True # SIM222
|
||||
|
||||
a or (1,) or True or (2,) # SIM222
|
||||
|
||||
a or tuple((1,)) or True or tuple((2,)) # SIM222
|
||||
|
||||
a or frozenset() or True # SIM222
|
||||
|
||||
a or frozenset(frozenset()) or True # SIM222
|
||||
|
||||
a or frozenset({1}) or True or frozenset({2}) # SIM222
|
||||
|
||||
a or frozenset(frozenset({1})) or True or frozenset(frozenset({2})) # SIM222
|
||||
|
||||
|
||||
# Inside test `a` is simplified.
|
||||
|
||||
bool(a or [1] or True or [2]) # SIM222
|
||||
|
||||
assert a or [1] or True or [2] # SIM222
|
||||
|
||||
if (a or [1] or True or [2]) and (a or [1] or True or [2]): # SIM222
|
||||
pass
|
||||
|
||||
0 if a or [1] or True or [2] else 1 # SIM222
|
||||
|
||||
while a or [1] or True or [2]: # SIM222
|
||||
pass
|
||||
|
||||
[
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
]
|
||||
|
||||
{
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
}
|
||||
|
||||
{
|
||||
0: 0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
}
|
||||
|
||||
(
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a or [1] or True or [2] # SIM222
|
||||
if b or [1] or True or [2] # SIM222
|
||||
)
|
||||
|
||||
# Outside test `a` is not simplified.
|
||||
|
||||
a or [1] or True or [2] # SIM222
|
||||
|
||||
if (a or [1] or True or [2]) == (a or [1]): # SIM222
|
||||
pass
|
||||
|
||||
if f(a or [1] or True or [2]): # SIM222
|
||||
pass
|
||||
|
||||
@@ -12,138 +12,3 @@ if a or False:
|
||||
|
||||
if False:
|
||||
pass
|
||||
|
||||
if a and f() and b and g() and False: # OK
|
||||
pass
|
||||
|
||||
if a and f() and False and g() and b: # SIM223
|
||||
pass
|
||||
|
||||
if False and f() and a and g() and b: # SIM223
|
||||
pass
|
||||
|
||||
if a and False and f() and b and g(): # SIM223
|
||||
pass
|
||||
|
||||
|
||||
if a or f() or b or g() or True: # OK
|
||||
pass
|
||||
|
||||
if a or f() or True or g() or b: # OK
|
||||
pass
|
||||
|
||||
if True or f() or a or g() or b: # OK
|
||||
pass
|
||||
|
||||
if a or True or f() or b or g(): # OK
|
||||
pass
|
||||
|
||||
|
||||
a and "" and False # SIM223
|
||||
|
||||
a and "foo" and False and "bar" # SIM223
|
||||
|
||||
a and 0 and False # SIM223
|
||||
|
||||
a and 1 and False and 2 # SIM223
|
||||
|
||||
a and 0.0 and False # SIM223
|
||||
|
||||
a and 0.1 and False and 0.2 # SIM223
|
||||
|
||||
a and [] and False # SIM223
|
||||
|
||||
a and list([]) and False # SIM223
|
||||
|
||||
a and [1] and False and [2] # SIM223
|
||||
|
||||
a and list([1]) and False and list([2]) # SIM223
|
||||
|
||||
a and {} and False # SIM223
|
||||
|
||||
a and dict() and False # SIM223
|
||||
|
||||
a and {1: 1} and False and {2: 2} # SIM223
|
||||
|
||||
a and dict({1: 1}) and False and dict({2: 2}) # SIM223
|
||||
|
||||
a and set() and False # SIM223
|
||||
|
||||
a and set(set()) and False # SIM223
|
||||
|
||||
a and {1} and False and {2} # SIM223
|
||||
|
||||
a and set({1}) and False and set({2}) # SIM223
|
||||
|
||||
a and () and False # SIM222
|
||||
|
||||
a and tuple(()) and False # SIM222
|
||||
|
||||
a and (1,) and False and (2,) # SIM222
|
||||
|
||||
a and tuple((1,)) and False and tuple((2,)) # SIM222
|
||||
|
||||
a and frozenset() and False # SIM222
|
||||
|
||||
a and frozenset(frozenset()) and False # SIM222
|
||||
|
||||
a and frozenset({1}) and False and frozenset({2}) # SIM222
|
||||
|
||||
a and frozenset(frozenset({1})) and False and frozenset(frozenset({2})) # SIM222
|
||||
|
||||
|
||||
# Inside test `a` is simplified.
|
||||
|
||||
bool(a and [] and False and []) # SIM223
|
||||
|
||||
assert a and [] and False and [] # SIM223
|
||||
|
||||
if (a and [] and False and []) or (a and [] and False and []): # SIM223
|
||||
pass
|
||||
|
||||
0 if a and [] and False and [] else 1 # SIM222
|
||||
|
||||
while a and [] and False and []: # SIM223
|
||||
pass
|
||||
|
||||
[
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
]
|
||||
|
||||
{
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
}
|
||||
|
||||
{
|
||||
0: 0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
}
|
||||
|
||||
(
|
||||
0
|
||||
for a in range(10)
|
||||
for b in range(10)
|
||||
if a and [] and False and [] # SIM223
|
||||
if b and [] and False and [] # SIM223
|
||||
)
|
||||
|
||||
# Outside test `a` is not simplified.
|
||||
|
||||
a and [] and False and [] # SIM223
|
||||
|
||||
if (a and [] and False and []) == (a and []): # SIM223
|
||||
pass
|
||||
|
||||
if f(a and [] and False and []): # SIM223
|
||||
pass
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
"yoda" <= compare # SIM300
|
||||
"yoda" < compare # SIM300
|
||||
42 > age # SIM300
|
||||
-42 > age # SIM300
|
||||
+42 > age # SIM300
|
||||
YODA == age # SIM300
|
||||
YODA > age # SIM300
|
||||
YODA >= age # SIM300
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# SIM910
|
||||
{}.get(key, None)
|
||||
|
||||
# SIM910
|
||||
{}.get("key", None)
|
||||
|
||||
# OK
|
||||
{}.get(key)
|
||||
|
||||
# OK
|
||||
{}.get("key")
|
||||
|
||||
# OK
|
||||
{}.get(key, False)
|
||||
|
||||
# OK
|
||||
{}.get("key", False)
|
||||
|
||||
# SIM910
|
||||
if a := {}.get(key, None):
|
||||
pass
|
||||
|
||||
# SIM910
|
||||
a = {}.get(key, None)
|
||||
|
||||
# SIM910
|
||||
({}).get(key, None)
|
||||
@@ -1,2 +0,0 @@
|
||||
def some_function():
|
||||
pass
|
||||
@@ -31,6 +31,3 @@ typing.TypedDict.anything()
|
||||
# import aliases are resolved
|
||||
import typing as totally_not_typing
|
||||
totally_not_typing.TypedDict
|
||||
|
||||
# relative imports are respected
|
||||
from .typing import TypedDict
|
||||
|
||||
11
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID251_false_positives.py
vendored
Normal file
11
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID251_false_positives.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# module members cannot be imported with that syntax
|
||||
import typing.TypedDict
|
||||
|
||||
# we don't track reassignments
|
||||
import typing, other
|
||||
typing = other
|
||||
typing.TypedDict()
|
||||
|
||||
# yet another false positive
|
||||
def foo(typing):
|
||||
typing.TypedDict()
|
||||
@@ -1,10 +1,8 @@
|
||||
from typing import TYPE_CHECKING, Any, ClassVar
|
||||
|
||||
|
||||
import attrs
|
||||
|
||||
from ....import unknown
|
||||
from ..protocol import commands, definitions, responses
|
||||
from ..server import example
|
||||
from .. import server
|
||||
from . import logger, models
|
||||
from ..protocol.UpperCaseModule import some_function
|
||||
@@ -1,3 +0,0 @@
|
||||
from typing import Tuple
|
||||
|
||||
x: Tuple
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user